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),但它不适用于&运算符。foo如果写成这样可能会起作用foo(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 字符数组的指针。一个微妙但重要的区别。

这揭示了test_const_array_pointer(&a)失败的原因。在 C 中匹配参数的确切规则是将它们视为对参数变量的赋值并遵循赋值规则。规范中 6.5.16.1 的相关约束是:

左操作数具有原子、限定或非限定指针类型,并且(考虑左操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,并且左侧指向的类型具有所有右边指向的类型的限定符;

在这种情况下,左操作数是指向 const 字符数组的指针,而右操作数是指向字符数组的指针。两者都是指针,但由于“字符数组”和“常量字符数组”不是兼容的类型,所以它失败了。

这最终类似于问题“为什么我不能将 a 传递char **给期望 a 的函数const char * const *”。在这两种情况下,由于函数不能修改参数直接指向的东西(因为它在双指针情况下也是 const ,因为它是一个数组,并且在你的情况下不能直接修改数组),它似乎是无害的——没有明确的演员表,没有办法默默地摆脱 const 。唯一的问题是标准委员会没有考虑它和/或他们没有选择编写匹配规则以便允许它。

于 2012-05-09T17:08:44.667 回答