分配给指针时从不兼容的指针类型警告初始化
当我使用这段代码时,GCC给我一个’来自不兼容指针类型的初始化’警告(虽然代码工作正常并且做了它应该做的事情,它打印了数组的所有元素)。
#include int main(void) { int arr[5] = {3, 0, 3, 4, 1}; int *p = &arr; printf("%p\n%p\n\n", p); for (int a = 0; a < 5; a++) printf("%d ", *(p++)); printf("\n"); }
但是当我使用这段代码时没有给出警告
int main(void) { int arr[5] = {3, 0, 3, 4, 1}; int *q = arr; printf("%p\n%p\n\n", q); for (int a = 0; a < 5; a++) printf("%d ", *(q++)); printf("\n"); }
这两个片段之间的唯一区别是我指定* p =&arr和* q = arr。
- 究竟有什么不同和制作?
- 为什么代码在两种情况下都会执行并提供完全相同的输出?
-
&arr
给出一个数组指针 ,一个特殊的指针类型int(*)[5]
,它指向整个数组。 -
arr
,当写在一个表达式中这样的int *q = arr;
,“衰变”成指向第一个元素的指针。 完全等同于int *q = &arr[0];
在第一种情况下,您尝试将int(*)[5]
分配给int*
。 这些是不兼容的指针类型,因此是编译器诊断消息。
事实certificate,数组指针和指向第一个元素的int指针很可能在内部具有相同的表示和相同的地址。 这就是为什么第一个例子“工作”即使它不正确C.
这些是指向(开始)数组(没有警告)的方法,两者都有效:
int *q = arr; /* OR */ int *q = &arr[0];
这个介于两者之间,会产生警告:
int *q = &arr;
输出是相同的,因为arr[0]
的地址实际上等同于指向arr[]
的指针。 初始化为指向arr[0]
任何指针都将其值作为arr[0]
的地址; 那就是指针。 阅读指针,特别是它们与数组的关系。 这里有无数的教程,其中一些可能会以两个案例为例。
TL; DR检查类型。
-
&arr
的类型为int (*) [5]
( 指向5个int
的数组的指针 )。 -
arr
的类型为int [5]
,但并非总是如此 。
引用C11
,章节§6.3.2.1,( 强调我的 )
除非它是
sizeof
运算符,_Alignof
运算符或一元&
运算符的操作数,或者是用于初始化数组的字符串文字,否则具有类型”数组类型”的表达式将转换为表达式输入”指向类型‘的指针 ,指向数组对象的初始元素,而不是左值。
因此,
int *q = arr; // int[5] decays to int *, == LHS
和
int *q = &arr[0]; // RHS == LHS
是相同的,而,
int *q = &arr; // LHS (int *) != RHS (int (*) [5])
是一种不匹配的类型表达式。
现在,它起作用,因为,正如Lundin的回答中已经提到的 ,数组变量的地址可能与数组的第一个元素的地址相同,所以尽管类型不匹配, 值也是相同的,所以这个似乎工作。
当在表达式中用作左值时,数组会衰减到指向其第一个元素的指针。 所以int *q = arr;
使用数组arr
的第一个元素的地址初始化int指针q
:all is fine,并且不会发出警告。
但是&arr
是数组的地址。 它只能正确地用于初始化(或分配)指向数组或相同大小的指针,或指向未确定大小的数组的指针。 您可以使用它初始化指向int的指针(这是一种不同且不兼容的类型),编译器会向您发出警告。 因为根据标准使用从指向不同类型的指针初始化的指针是Undefined Behavior。
但是在常见的实现中,指向任何类型的指针都具有相同的表示forms,即对象的第一个字节的地址。 因此,即使标准不允许,指令int *p = arr;
以与p
相同的值结束,得到正确的int *p = arr;
。 这就解释了为什么你的程序仍然给出了预期值。
BTW,未定义的行为并不禁止预期的结果,只是一个不同的编译器可以给出不同的结果,崩溃,过早结束而没有错误,踢你的狗等等(即使没有编译器可以打我的狗到现在;-))