我有这个opaque类型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草案部分的提示)。
答案 0 :(得分:3)
这是因为数组不是C中的第一类类型。因为它们不是第一类类型,所以具有类型限定符(例如{{1})实际上没有任何意义。在数组上 - 由于无法直接分配或使用数组,限定符将不起作用。因此,当您尝试将限定符应用于数组时,它会以静默方式移动到数组元素。因此,当你说const
时,你没有得到指向const数组的字符的指针,你得到一个指向const字符数组的指针。一个微妙但重要的区别。
这揭示了const array_t *
失败的原因。在C中匹配参数的确切规则是将它们视为参数变量的赋值,并遵循赋值规则。规范中6.5.16.1的相关约束是:
左操作数具有原子,限定或非限定指针类型,并且(考虑到 两个操作数都是左值操作数在左值转换后的类型 指向兼容类型的限定或非限定版本的指针,以及指向的类型 左边的所有限定符都是右边所指示的类型;
在这种情况下,左操作数是指向const字符数组的指针,而右操作数是指向字符数组的指针。两者都是指针,但作为“字符阵列”和 “const chars数组”不是兼容类型,它失败了。
这最终类似于“为什么我不能将test_const_array_pointer(&a)
传递给期望char **
”的函数。在这两种情况下,由于函数不能修改参数直接指向的东西(因为它在双指针的情况下也是const,因为它的数组和数组不能直接在你的情况下修改),它似乎是无害的 - 如果没有明确的演员,就没有办法无声地摆脱const。唯一的问题是它没有被标准委员会考虑和/或他们没有选择编写匹配的规则以便允许它。