2008-09-16 31 views
55

次のコードは、(正しく)(バージョン3.4.5および4.2.1でテストGCC & G ++それぞれを用いて、MSVCは気にしていないよう)Cで警告およびC++でエラーを与える:Cで 'char **'を 'const char * const *'に変換できないのはなぜですか?

char **a; 
const char** b = a; 

私はこれを理解して受け入れることができます。
この問題に対するC++の解決策は、bをconst char * const *に変更することです。これは、ポインタの再割り当てを禁止し、const-correctness(C++ FAQ)を回避できないようにします。

char **a; 
const char* const* b = a; 

しかし、純粋なC、(のconst char型を使用して* CONST *)修正版ではまだ警告を与え、私は理由を理解していません。 キャストを使用しないでこれを回避する方法はありますか?

明確にする:
1)なぜCで警告が生成されるのですか?これは完全に安全でなければならず、C++コンパイラはそれをそのように認識しているようです。
2)この文字を指している文字を変更しないと言っている間に、このchar **をパラメータとして受け入れる正しい方法は何でしょうか? たとえば、私は機能書きたい場合:

void f(const char* const* in) { 
    // Only reads the data from in, does not write to it 
} 

をそして私は、文字**上でそれを呼び出すためにどのようなパラメータの正しい型になりたかったですか?

編集: 特に質問に対処したり、回答に応じた人に感謝します。

私は、できるかどうかにかかわらず、キャストなしではできないという答えを受け入れました。

+0

私はあなたの質問については混乱しています。「Cさんに警告しないようにするにはどうすればいいですか?それとも「C++にコンパイルエラーを投げないようにするには?あるいは、どちらもそうではないかもしれません。 – user7116

+0

私はsixlettervariablesに同意しますが、この質問はさらに明確にする必要があります。 –

+0

質問は "なぜ私は 'char **'を 'char * const *'に変換できないのですか? – Kevin

答えて

53

私は数年前にこの同じ問題を抱えていました。

Cの規則はより簡単に記述されています(つまり、char**からconst char*const*に変換するなどの例外をリストしません)。結論として、それは許されないだけです。 C++標準では、このようなケースを可能にするためのルールが多く含まれていました。

最後に、これはC標準の単なる問題です。私は、次の標準(または技術報告書)がこれに対処することを願っています。

+0

なぜダウン投票しましたか?説明してください! – Kevin

+0

私は確かにそれを投票しなかった。それは私が今までに得た最も関連性の高い答えです。私が100の評判ポイントを持っていれば、私はこれを投票するでしょう。 – HappyDude

+1

@Kevin:もしあなたがまだ周りにいるなら、私は、それが規格の単なる問題であることがあなたの答えについてどのくらい確かであるのか疑問に思っています。 もしそうなら、私はこの質問をもうちょっと閉じることができます。これは私が得るほど多くの答えです。 – HappyDude

11

>はしかし、純粋なCで、これはまだ警告を与え、あなたはすでに問題を特定した理由

私は理解していない - このコードは、constの-正しくありません。 "Const correct"は、constを取り除いているconst_castとCスタイルのキャストを除いて、それらのconstポインタまたは参照によってconstオブジェクトを決して変更できないことを意味します。

const-correctness-constの値は、大部分の場合、プログラマーのエラーを検出するためのものです。 constとして何かを宣言した場合は、それを変更すべきでないと思っています。少なくとも、constバージョンへのアクセス権しか持たない人は、変更できないはずです。考えてみましょう:

宣言したよう
void foo(const int*); 

は、fooは整数引数によって指さ修正する許可を持っていません。

char *y; 

char **a = &y; // a points to y 
const char **b = a; // now b also points to y 

// const protection has been violated, because: 

const char x = 42; // x must never be modified 
*b = &x; // the type of *b is const char *, so set it 
     //  with &x which is const char* .. 
     //  .. so y is set to &x... oops; 
*y = 43; // y == &x... so attempting to modify const 
     //  variable. oops! undefined behavior! 
cout << x << endl; 

非const型は唯一のconstに変換することができます:

あなたが投稿したコードは、constの-正しくない理由がわからない場合は、HappyDudeのコードからわずかに異なる次のコードを検討明示的にキャストすることなくデータタイプに対して「const」の迂回を防ぐ特別な方法をタイプします。

最初にconst宣言されたオブジェクトは特に特別です。コンパイラは決して変更されないと見なすことができます。しかし、 'b'にキャストなしの 'a'の値を割り当てることができれば、意図せずにconst変数を変更しようとする可能性があります。これは、あなたがコンパイルするように頼んだチェックを破るだけでなく、変数の値を変更できないようにするだけでなく、コンパイラの最適化を破ることもできます!

一部のコンパイラでは、 '42'、 '43'などが表示され、プログラムによってはクラッシュします。

編集-追加:

HappyDude:あなたのコメントは上のスポットです。 Cランゲージまたは使用しているCコンパイラは、const char * const *をC++言語で処理するのとは根本的に違って扱います。おそらく、このソース行だけのコンパイラ警告を消音することを検討してください。

編集-削除:取り外しタイプミス

+4

あなたが言ったことは正しいですが、質問には答えません。 私が言ったように、私はなぜchar **からconst char **への変換が間違っているのか理解しています。 私が理解できないのは、char **からconst char * const *への変換が間違っている理由です。 私は解明するために私の質問を更新しました。 – HappyDude

+3

このレスポンスが投票され続けるのは面白いことです。私は、多くの人が質問の最初の2行を過ぎて読んでいないと思う、私は未来を念頭に置いておくだろう。 これは比較的あいまいな点でよく書かれた投稿ですが、私はそれが適切であることを望みます:P。 – HappyDude

+3

@Aaron:あなたの投稿は特にトピックには関係しませんが、よく書かれています。 – user7116

0

私は暗黙のうちに** constのために文字をキャストする際のエラーを取得することはできませんよのchar * constの*、少なくともMSVC 14(VS2k5)とg ++ 3.3に。 3。 GCC 3.3.3では警告が出されていますが、正しく実行されているかどうかはわかりません。

test.cの:Cコードとしてコンパイルと

#include <stdlib.h> 
#include <stdio.h> 
void foo(const char * const * bar) 
{ 
    printf("bar %s null\n", bar ? "is not" : "is"); 
} 

int main(int argc, char **argv) 
{ 
    char **x = NULL; 
    const char* const*y = x; 
    foo(x); 
    foo(y); 
    return 0; 
} 

出力:C++コードとしてコンパイルとCL/TC/W4/Wp64 test.cの

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter 
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

出力:CL/TP/W4/Wp64 test.cの

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter 
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

のgccで出力:gccの-Wall test.cの

グラムとが
test2.c: In function `main': 
test2.c:11: warning: initialization from incompatible pointer type 
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type 

出力:G ++ -Wall test.cの

何も出力が

+0

申し訳ありませんが、私はgccでコンパイルしているはずです。 これを明確にするために質問を更新しました。 幸いにも(あるいはそうでないかもしれませんが)私はほとんどmsvcで扱いにくい警告に遭遇することはほとんどありません。 – HappyDude

+0

@HappyDude:GCCの調査結果を更新しました。そして、今あなたが言っていることが分かりますが、私はまだコンパイラの警告に同意していません。私はC仕様を打ち破り、私が見つけたものを見るつもりです。 – user7116

+0

@sixlettervariables:ありがとうございました!あなたが何かを見つけたら教えてください。しかし、私はケビンが言ったことは正しいと思っています。それはただの基準ではカバーされていません。 – HappyDude

0

私はデータを意味するものではありませんconstキーワードを変更することができないと確信している/定数ではありません、データは読み取り専用として扱われます。これを考えてみましょう:

const volatile int *const serial_port = SERIAL_PORT; 

これは有効なコードです。 volatileとconstはどのように共存できますか?シンプル。 volatileは、データを使用するときに常にメモリを読み取るようにコンパイラに指示し、constはserial_portポインタを使用してメモリに書き込もうとするとエラーを生成するようコンパイラに指示します。

constはコンパイラのオプティマイザを助けますか?いいえ、全くありません。 constはデータをキャストすることで追加したり削除したりすることができるので、constデータが実際には一定であるかどうかはわかりません(キャストは別の変換単位で実行できるので)。C++では、問題をさらに複雑にする可変キーワードもあります。

char *const p = (char *) 0xb000; 
//error: p = (char *) 0xc000; 
char **q = (char **)&p; 
*q = (char *)0xc000; // p is now 0xc000 

実際には読み取り専用のメモリ(ROMなど)に書き込もうとすると、おそらく標準で定義されていない可能性があります。

+2

これは本当に問題に対処していません。 私が理解しているように、キャストによるconstの削除は、ほとんどのコンパイラが許可し、「正しいことをする」場合でも、技術的にはCの標準に従った定義されていない操作です。 しかし私はまったく尋ねていることではありません。 – HappyDude

+2

@HappyDudeのキャスト離脱constは明確に定義されています。 'const'(または文字列リテラルのような書き込み不可能)として宣言されたオブジェクトに実際に書き込もうとすると、定義されません。 –

9

互換性があるとみなされるためには、ソースポインタは直前の間接レベルでconstにする必要があります。

char **a; 
const char* const* b = a; 

しかし、これはしません::だから、これはあなたのGCCに警告与える

また
const char **a; 
const char* const* b = a; 

を、あなたはそれをキャストすることができます:

char **a; 
const char* const* b = (const char **)a; 

あなたが同じが必要になりますあなたが述べたように関数f()を呼び出すためにキャストします。私の知る限り、この場合暗黙の変換を行う方法はありません(C++を除く)。

+0

より詳しく質問をお読みください。 私は、最初のコードスニペットが間違っていることを知っています。 しかし、もう1つは、(私が知る限りは)うまくいくはずです。私が言及したように、MSVCは警告を出さないが、gccは警告を出す。 – HappyDude

+0

申し訳ありませんが、私が欲しかったことをうまく説明していないし、コードサンプルが正確であることを確認するためにGCCの結果をテストしませんでした。修正する。 –

+0

Fabioに感謝します。編集したレスポンスははるかに明確です。 私はそれをキャストする必要があることを受け入れなければならないと思う。 – HappyDude

1

これは迷惑ですが、あなたはリダイレクトの別のレベルを追加するために喜んでいる場合、あなたは多くの場合、ポインタへのポインタに押し下げするには、以下のことが可能です。

char c = 'c'; 
char *p = &c; 
char **a = &p; 

const char *bi = *a; 
const char * const * b = &bi; 

それはわずかに異なるがあります意味はありますが、通常は実行可能であり、キャストは使用しません。

関連する問題