18

概要

Microsoft Visual Studio の C/C++ コンパイラは、C プログラムがポインターをデータへのポインター (または など)に変換しようとすると、警告C4090を生成します (そのような型は実際には へのポインターではありません)。さらに奇妙なことに、同じコンパイラが、C++ としてコンパイルされた同一のコードを暗黙のうちに受け入れます。constconst 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

それでも、それが意図的である場合、次のようになります。

  1. C でこの警告を生成するコードが C++ でエラーを生成すると Microsoft のドキュメントが示しているのはなぜですか?

  2. 警告を無視または抑制する以外に、(私の現実の状況のように) データへfreeの非ポインターへの非constポインターでなければならない場合、合理的な代替手段はありますか? C++ でこのようなことが起こった場合、可変引数リストに渡された文字列を、配列ではなく高レベルの STL コンテナーに格納できます。C++ STL にアクセスせず、それ以外の場合は高レベルのコレクションを使用しない C プログラムの場合、そのようなことは妥当なオプションではありません。constconst

  3. 一部のプログラマーは、警告をエラーとして扱うという企業/組織のポリシーの下で働いています。C4090は であっても有効/W1です。人々は以前にこれに遭遇したに違いありません。それらのプログラマーは何をしますか?

4

2 に答える 2

11

どうやらこれは単にVC++のバグです。

const char **x;結果がcharsへの「読み取り専用」ポインタへのポインタであり、それ自体が「読み取り専用」ポインタではないことを宣言した場合( const-ness用語は間違った概念をプッシュするため、「読み取り専用」という用語を使用します。指し示されている文字は一定ですが、これは一般に誤りです...const参照とポインターを使用することは、参照またはポインターのプロパティであり、指し示されているデータまたは参照されているデータの恒常性については何も伝えません。

読み取り/書き込みポインタはすべてに変換でき、VC ++には、モードでもモードでもvoid *、そのコードをコンパイルするときに警告を発する本当の理由はありません。CC++

これは正式には問題ではないことに注意してください。標準では、どの警告を発行するか、発行しないかが義務付けられていないため、コンパイラは、完全に有効なコードが準拠している場合に警告を発することができます。VC ++は、実際には、有効なC++コードに対してこれらの警告を大量に発行します...

于 2012-05-01T20:34:12.743 に答える
1

6502 のように、これはコンパイラのバグのようです。しかし、あなたはそれについて何をすべきかについても尋ねます。

私の答えは、無料呼び出しに明示的なキャストを追加してから、それが必要な理由を説明するコメントを追加する必要があるということです。コンパイラのバグは発生します。最も簡単な回避策を使用し、後でバグが解決された場合にテストできるようにメモを追加してください。

バグをコンパイラベンダーに報告するための追加ポイント。

const T *1 については、暗黙的に aを aにキャストすることを参照しているようです。これはvoid *、C では警告、C++ ではエラーになるはずです。

于 2012-05-01T21:23:36.783 に答える