概要
Microsoft Visual Studio の C/C++ コンパイラは、C プログラムがポインターをデータへのポインター (または など)に変換しようとすると、警告C4090を生成します (そのような型は実際には へのポインターではありません)。さらに奇妙なことに、同じコンパイラが、C++ としてコンパイルされた同一のコードを暗黙のうちに受け入れます。const
const void **
const char **
void *
const
この不一致の理由は何ですか? また、(他のコンパイラとは異なり) Visual Studio がポインターからポインターへの暗黙的な変換に問題があるのはconst
なぜvoid *
ですか?
詳細
va_arg
可変引数リストに渡された C 文字列が (呼び出されるループによって) 配列に読み込まれる C プログラムがあります。C-string は typeconst char *
であるため、それらを追跡する配列は typeconst char **
です。コンテンツを含む文字列へのポインターのこの配列は、const
それ自体が動的に ( でcalloc
)割り当てられfree
、関数が戻る前 (C 文字列が処理された後) に割り当てられます。
このコードをcl.exe
(Microsoft Visual C++ で) コンパイルすると、警告レベルが低くても、free
呼び出しによって警告C4090がトリガーされました。free
は を取るのでvoid *
、これはコンパイラが私が を に変換したことを好まなかったことを教えてくれましconst char **
たvoid *
。const void **
これを確認するために、 aを aに変換しようとする簡単な例を作成しましたvoid *
。
/* cast.c - Can a const void** be cast implicitly to void* ? */
int main(void)
{
const void **p = 0;
void *q;
q = p;
return 0;
}
次に、次のようにコンパイルし、これが警告の原因であることを確認しました。
>cl cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
cast.c
cast.c(7) : warning C4090: '=' : different 'const' qualifiers
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:cast.exe
cast.obj
警告 C4090 に関するMicrosoft のドキュメントには、次のように記載されています。
この警告は、C プログラムに対して発行されます。C++ プログラムでは、コンパイラはエラー C2440 を発行します。
C++ は C よりも強く型付けされた言語であり、C で許可されている潜在的に危険な暗黙のキャストは C++ では許可されていないため、これは理にかなっています。Microsoft のドキュメントでは、C++ でエラーC2440をトリガーするのと同じコードまたはコードのサブセットに対して、C で警告C2440がトリガーされるように見えます。
または、テストプログラムを C++ としてコンパイルしようとするまで (/TP
フラグはこれを行います)、次のように考えていました。
>cl /TP cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:cast.exe
cast.obj
同じコードを C++ としてコンパイルすると、エラーや警告は発生しません。確かに、再構築して、コンパイラに可能な限り積極的に警告するように指示しました。
>cl /TP /Wall cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:cast.exe
cast.obj
それは黙って成功します。
これらのビルドは、Windows 7 マシン上の Microsoft Visual C++ 2010 Express Editioncl.exe
で行われましたが、Windows XP マシンでは、Visual Studio .NET 2003cl.exe
と Visual C++ 2005 Express Edition の両方で同じエラーが発生しますcl.exe
。したがって、これはすべてのバージョンで発生するようで (可能なすべてのバージョンでテストしたわけではありません)、マシンでの Visual Studio のセットアップ方法に問題はありません。
同じコードは、Ubuntu 11.10 システム (バージョン string ) 上の GCC 4.6.1 で問題なくコンパイルgcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
され、C89、C99、および C++ として可能な限り積極的に警告するように設定されています。
$ gcc -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘main’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]
$ gcc -std=c99 -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘main’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]
$ g++ -x c++ -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘int main()’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]
q
割り当てられた後に決して読み取られないことを警告しますが、その警告は意味があり、無関係です。
すべての警告が有効になっている GCC で警告をトリガーせず、GCC または MSVC のいずれかで C++ で警告をトリガーしないことに加えて、ポインターからポインターへのポインターから const への変換void *
はまったく問題と見なされるべきではないように思えますvoid *
。 non-const
へのポインタ、const へのポインタへのポインタも non- へのポインタconst
です。
私の実際のコード (例ではありません) では、#pragma
ディレクティブまたは明示的なキャストを使用するか、C++ としてコンパイルすることでこれを黙らせることができます (へへへ)、または無視することもできます。しかし、少なくともなぜこれが起こっているのかを理解する前に、私はそれらのことをしたくありません. (そして、なぜ C++ では起こらないのか!)
void *
C++ とは異なり、C では任意のポインタからデータ型への暗黙的なキャストが可能です。したがって、ポインタを から に暗黙的に変換し、次に からconst char **
にvoid *
暗黙的に変換するvoid *
ことchar **
で、ポインタが指す定数データをキャストなしで変更できます。それは悪いでしょう。しかし、それが、C のより弱い型安全性によって許容される他のあらゆる種類のことよりも悪いことだとは思いません。
void
非ポインター型が に変換されたときに警告しないという選択を考えると、この警告は理にかなっていると思いvoid *
ます。
/* cast.c - Can a const void** be cast implicitly to void* ? */
int main(void)
{
const void **p = 0;
void *q;
q = p;
return 0;
}
>cl /Wall voidcast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
voidcast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:voidcast.exe
voidcast.obj
それでも、それが意図的である場合、次のようになります。
C でこの警告を生成するコードが C++ でエラーを生成すると Microsoft のドキュメントが示しているのはなぜですか?
警告を無視または抑制する以外に、(私の現実の状況のように) データへ
free
の非ポインターへの非const
ポインターでなければならない場合、合理的な代替手段はありますか? C++ でこのようなことが起こった場合、可変引数リストに渡された文字列を、配列ではなく高レベルの STL コンテナーに格納できます。C++ STL にアクセスせず、それ以外の場合は高レベルのコレクションを使用しない C プログラムの場合、そのようなことは妥当なオプションではありません。const
const
一部のプログラマーは、警告をエラーとして扱うという企業/組織のポリシーの下で働いています。C4090は であっても有効
/W1
です。人々は以前にこれに遭遇したに違いありません。それらのプログラマーは何をしますか?