60

次のコード スニペットは (正しく) C で警告を出し、C++ でエラーを出します (それぞれ gcc と g++ を使用し、バージョン 3.4.5 と 4.2.1 でテストされています; MSVC は気にしないようです):

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で警告を生成するのですか?これは完全に const セーフである必要があり、C++ コンパイラはそれをそのように認識しているようです。
2) この char** をパラメーターとして受け入れ、それが指す文字を変更しないと言いながら (そしてコンパイラーに強制させる) 正しい方法は何ですか? たとえば、関数を書きたい場合:

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

そして、char** でそれを呼び出したかったのですが、パラメーターの正しい型は何でしょうか?

4

6 に答える 6

59

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

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

結局のところ、これは C 標準の問題にすぎません。次の標準 (またはテクニカル レポート) がこれに対処することを願っています。

于 2008-09-16T23:04:21.097 に答える
10

互換性があると見なされるには、ソース ポインターは直前の間接レベルで const である必要があります。したがって、これにより GCC で警告が表示されます。

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

しかし、これはしません:

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

または、次のようにキャストできます。

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

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

于 2008-09-17T00:06:21.853 に答える
10

ただし、純粋なCでは、これでも警告が表示され、理由がわかりません

あなたはすでに問題を特定しました -- このコードは const-correct ではありません。「正しい定数」とは、const_castおよび C スタイルのキャストによる の削除を除いて、これらの const ポインタまたは参照を介しconstてオブジェクトを変更できないことを意味します。const

const-correctness --の値はconst、大部分、プログラマーのエラーを検出するためにあります。何かを として宣言する場合はconst、それを変更する必要がないと考えていることを示しています。少なくとも、constバージョンにのみアクセスできる人は変更できないはずです。検討:

void foo(const int*);

宣言されているように、引数が指す整数を変更する 権限fooがありません。

投稿したコードが正しくない理由がわからない場合はconst、次のコードを検討してください。HappyDude のコードとはわずかに異なります。

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

最初に宣言されたオブジェクトconstは特に特別です。コンパイラは、オブジェクトが決して変更されないと想定できます。ただし、キャストなしbで の値を割り当てることができる場合、誤って変数aを変更しようとする可能性があります。constこれは、変数値の変更を禁止するために、コンパイラに要求したチェックを中断するだけでなく、コンパイラの最適化を中断することもできます!

一部のコンパイラでは、これは出力され、一部のコンパイラでは4243プログラムがクラッシュします。

編集追加:

HappyDude: あなたのコメントは的確です。C 言語または使用している C コンパイラはconst char * const *、C++ 言語とは根本的に異なる方法で処理します。おそらく、このソース行のみのコンパイラ警告を黙らせることを検討してください。

于 2008-09-16T23:08:36.500 に答える
1

これは煩わしいことですが、別のレベルのリダイレクトを追加する場合は、多くの場合、次のようにしてポインターからポインターにプッシュダウンできます。

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

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

意味は少し異なりますが、通常は機能し、キャストは使用しません。

于 2008-09-19T00:41:00.083 に答える
0

constキーワードは、データが変更できない/一定であることを意味するのではなく、データが読み取り専用として扱われることだけを意味していると確信しています。このことを考慮:

const volatile int *const serial_port = SERIAL_PORT;

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

constはコンパイラのオプティマイザに役立ちますか?いいえ、まったくありません。constnessはキャストによってデータに追加したり、データから削除したりできるため、コンパイラーはconstデータが本当に一定であるかどうかを判断できません(キャストは別の変換単位で実行できるため)。C ++では、問題をさらに複雑にするための可変キーワードもあります。

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

実際に読み取り専用のメモリ(たとえばROM)に書き込もうとするとどうなるかは、おそらく標準ではまったく定義されていません。

于 2008-09-16T23:46:37.657 に答える
0

少なくとも MSVC 14 (VS2k5) および g++ 3.3.3 では、char** を const char * const * に暗黙的にキャストしてもエラーが発生しません。GCC 3.3.3 は警告を発行しますが、それが正しいかどうかは正確にはわかりません。

test.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

C++ コードとしてコンパイルして出力: 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++ で出力: g++ -Wall test.C

出力なし

于 2008-09-16T23:42:06.250 に答える