3

この不透明な型と、呼び出し先type_tのような関数プロトタイプがあります。foo(type_t *t)

int bar(void)
{
    type_t t;

    foo(&t);

    return 0;
}

foo(type_t *t)関数プロトタイプを からに変更したかったのfoo(const type_t *t)です。

残念ながらtype_t、 ) などの配列として定義されているため、引数を指定して関数をtypedef char type_t[16]呼び出すと、コンパイラに警告が生成されます。foo&t

そもそもfoo関数は のようなプロトタイプを持ち、 で呼び出す必要がfoo(type_t t)ありますfoo(t)。この場合、ポインター規則への配列の減衰も許可されることを望んでいましたが、演算子foo(&t)には適用されません。書かれ&ていれば、おそらくうまくいくでしょうfoofoo(void *t)

注: 詳細な例はスキップできます。最後を参照してください。

そこで、警告/エラーを再現するこの小さなテスト プログラムを作成しましたhttps://gist.github.com/2644970

GCC バージョン 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1) では、次の警告が生成されます。

array.c: In function ‘test_array_pointer’:
array.c:36: warning: return makes integer from pointer without a cast
array.c: In function ‘test_const_array_pointer’:
array.c:59: warning: return makes integer from pointer without a cast
array.c: In function ‘main’:
array.c:132: warning: passing argument 1 of ‘test_array_pointer’ from incompatible pointer type
array.c:18: note: expected ‘uint8_t (*)[16]’ but argument is of type ‘uint8_t *’
array.c:134: warning: passing argument 1 of ‘test_const_array_pointer’ from incompatible pointer type
array.c:41: note: expected ‘const uint8_t (*)[16]’ but argument is of type ‘uint8_t (*)[16]’
array.c:135: warning: passing argument 1 of ‘test_const_array_pointer’ from incompatible pointer type
array.c:41: note: expected ‘const uint8_t (*)[16]’ but argument is of type ‘uint8_t *’
array.c:137: warning: passing argument 1 of ‘test_array’ from incompatible pointer type
array.c:64: note: expected ‘uint8_t *’ but argument is of type ‘uint8_t (*)[16]’
array.c:140: warning: passing argument 1 of ‘test_const_array’ from incompatible pointer type
array.c:82: note: expected ‘const uint8_t *’ but argument is of type ‘uint8_t (*)[16]’
array.c:143: warning: passing argument 1 of ‘test_const_pointer’ from incompatible pointer type
array.c:100: note: expected ‘const uint8_t *’ but argument is of type ‘uint8_t (*)[16]’

LLVM/Clang バージョン 1.1 (branches/release_27) はこれらを生成しています:

array.c:36:10: warning: incompatible pointer to integer conversion returning 'array_t' (aka 'uint8_t [16]'), expected 'uintptr_t' (aka 'unsigned int') [-pedantic]
  return a[0]; /* warning: return makes integer from pointer without a cast */
         ^~~~
array.c:59:10: warning: incompatible pointer to integer conversion returning 'array_t const' (aka 'uint8_t const[16]'), expected 'uintptr_t' (aka 'unsigned int') [-pedantic]
  return a[0]; /* warning: return makes integer from pointer without a cast */
     ^~~~
array.c:132:3: warning: incompatible pointer types passing 'array_t' (aka 'uint8_t [16]'), expected 'array_t *' [-pedantic]
  TEST(array_pointer, a);
  ^~~~~~~~~~~~~~~~~~~~~~
array.c:132:23: note: instantiated from:
  TEST(array_pointer, a);
                      ^
array.c:134:3: warning: incompatible pointer types passing 'array_t *', expected 'array_t const *' [-pedantic]
  TEST(const_array_pointer, &a);
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
array.c:134:29: note: instantiated from:
  TEST(const_array_pointer, &a);
                            ^~
array.c:135:3: warning: incompatible pointer types passing 'array_t' (aka 'uint8_t [16]'), expected 'array_t const *' [-pedantic]
  TEST(const_array_pointer, a);
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
array.c:135:29: note: instantiated from:
  TEST(const_array_pointer, a);
                            ^
array.c:137:3: warning: incompatible pointer types passing 'array_t *', expected 'uint8_t *' [-pedantic]
  TEST(array, &a);
  ^~~~~~~~~~~~~~~
array.c:137:15: note: instantiated from:
  TEST(array, &a);
              ^~
array.c:140:3: warning: incompatible pointer types passing 'array_t *', expected 'uint8_t const *' [-pedantic]
  TEST(const_array, &a);
  ^~~~~~~~~~~~~~~~~~~~~
array.c:140:21: note: instantiated from:
  TEST(const_array, &a);
                    ^~
array.c:143:3: warning: incompatible pointer types passing 'array_t *', expected 'uint8_t const *' [-pedantic]
  TEST(const_pointer, &a);
  ^~~~~~~~~~~~~~~~~~~~~~~
array.c:143:23: note: instantiated from:
  TEST(const_pointer, &a);
                      ^~
8 diagnostics generated.

注:縮小版

この最後の例を参照してください。

typedef char array_t[16];

static int
test_const_array_pointer(const array_t *a)
{
    return 0;
}

int
main(void)
{
    array_t a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
    const array_t b = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };

    test_const_array_pointer(&a); /* warning: passing argument 1 of ‘test_const_array_pointer’ from incompatible pointer type
                                     note: expected ‘const char (*)[16]’ but argument is of type ‘char (*)[16]’ */

    test_const_array_pointer(a); /* warning: passing argument 1 of ‘test_const_array_pointer’ from incompatible pointer type
                                     note: expected ‘const char (*)[16]’ but argument is of type ‘char *’ */

    test_const_array_pointer(&b); /* OK */

    test_const_array_pointer(b); /* warning: passing argument 1 of ‘test_const_array_pointer’ from incompatible pointer type
                                    note: expected ‘const char (*)[16]’ but argument is of type ‘const char *’ */

    return 0;
}

&a は &b と同等だと思いました。たとえば、関数foo(const char *)に与えるプロトタイプがある場合は必要ありません。const char *たとえば、渡すことchar *が受け入れられます。

だから私の質問は、なぜ のような引数が配列const array_t *へのポインタを必要とするのですか? const(C11 ドラフト セクションへのポインタをいただければ幸いです)。

4

1 に答える 1

3

これは、C では配列がファーストクラスの型ではないという事実の影響です。配列はファーストクラスの型ではないため、配列に型修飾子 ( などconst) を使用しても意味がありません。配列に直接割り当てたり使用したりすることはできません。修飾子は効果がありません。したがって、修飾子を配列に適用しようとすると、暗黙のうちに配列要素に移動されます。その結果、あなたが言うときconst array_t *、文字の const 配列へのポインターを取得しているのではなく、const char の配列へのポインターを取得しています。微妙だが重要な違い。

これにより、失敗する理由が明らかになりtest_const_array_pointer(&a)ます。C で引数を一致させる正確な規則は、それらがパラメーター変数への代入として扱われ、代入の規則に従うことです。仕様の 6.5.16.1 からの関連する制約は次のとおりです。

左のオペランドがアトミック、修飾、または非修飾のポインター型を持ち、(左辺値変換後に左のオペランドが持つ型を考慮して) 両方のオペランドが互換性のある型の修飾または非修飾バージョンへのポインターであり、左が指す型がすべてを持っている右が指す型の修飾子。

この場合、左側のオペランドは const char の配列へのポインターであり、右側のオペランドは char の配列へのポインターです。どちらもポインタですが、「文字の配列」と「定数文字の配列」は互換性のある型ではないため、失敗します。

char **これは、「 a を期待する関数に aを渡すことができないのはなぜですか」という質問に似ていconst char * const *ます。どちらの場合も、関数はパラメーターが指すものを直接変更できないため (ダブルポインターの場合は const であるため、配列と配列を直接変更できないため)、次のように見えます。無害であること -- 明示的なキャストなしに、黙って const を取り除く方法はありません。唯一の問題は、標準化委員会によって考慮されなかったこと、および/または標準化委員会が許可されるように一致ルールを作成することを選択しなかったことです。

于 2012-05-09T17:08:44.667 に答える