分配给指针时从不兼容的指针类型警告初始化

当我使用这段代码时,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,未定义的行为并不禁止预期的结果,只是一个不同的编译器可以给出不同的结果,崩溃,过早结束而没有错误,踢你的狗等等(即使没有编译器可以打我的狗到现在;-))