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

C 从不兼容的指针类型初始化分配给指针时出现警告,c,arrays,pointers,C,Arrays,Pointers,当我使用这段代码时,GCC会给我一个“从不兼容的指针类型初始化”警告(尽管代码运行良好,并且执行了它应该执行的操作,即打印数组的所有元素) #包括 内部主(空) { int-arr[5]={3,0,3,4,1}; int*p=&arr; printf(“%p\n%p\n\n”,p); 对于(int a=0;a

当我使用这段代码时,GCC会给我一个“从不兼容的指针类型初始化”警告(尽管代码运行良好,并且执行了它应该执行的操作,即打印数组的所有元素)

#包括
内部主(空)
{
int-arr[5]={3,0,3,4,1};
int*p=&arr;
printf(“%p\n%p\n\n”,p);
对于(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");
}
int main(无效)
{
int-arr[5]={3,0,3,4,1};
int*q=arr;
printf(“%p\n%p\n\n”,q);
对于(int a=0;a<5;a++)
printf(“%d”,*(q++);
printf(“\n”);
}
这两个代码段之间的唯一区别是我分配了*p=&arr和*q=arr

  • 到底是什么不同的&制造
  • 为什么代码在这两种情况下执行并给出完全相同的输出

输出是相同的,因为
arr[0]
的地址实际上相当于指向
arr[]
的指针。任何初始化为指向
arr[0]
的指针的值都是
arr[0]
的地址;这就是指针。阅读指针,尤其是指针与数组的关系。这里有无数的教程,其中一些可能以您的两个案例为例。

这些是指向数组(开头)的方法(没有警告),这两种方法都有效:

int *q = arr;
/* OR */
int *q = &arr[0];
这一个介于两者之间,将生成一个警告:

int *q = &arr;

TL;DR检查类型

  • &arr
    的类型为
    int(*)[5]
    (指向5
    int
    s数组的指针)
  • arr
    属于
    int[5]
    类型,但并不总是这样
引用第§6.3.2.1章中的
C11
(重点)

除非它是
sizeof
运算符、
\u Alignof
运算符或 一元
&
运算符,或者是用于初始化数组的字符串文字,一个具有 类型“”的数组“”已转换为类型为“”指向类型“”的指针的表达式 指向数组对象的初始元素,并且不是左值

因此,

 int *q = arr;   // int[5] decays to int *, == LHS

是相同的,然而

 int *q = &arr; // LHS (int *) != RHS (int (*) [5])
是不匹配的类型表达式

现在,它可以工作了,因为如中所述,数组变量的地址很可能与数组的第一个元素的地址相同,因此尽管类型不匹配,但值是相同的,因此这似乎可以工作。

  • &arr
    给出一个数组指针,一种特殊的指针类型
    int(*)[5]
    指向整个数组
  • arr
    ,当用
    int*q=arr这样的表达式编写时,“衰减”为指向第一个元素的指针。完全等同于
    int*q=&arr[0]
在第一种情况下,您尝试将
int(*)[5]
分配给
int*
。这些是不兼容的指针类型,因此会出现编译器诊断消息


事实证明,数组指针和指向第一个元素的int指针在内部很可能具有相同的表示形式和相同的地址。这就是为什么第一个示例“有效”,即使它不是正确的C。

当在表达式中用作左值时,数组衰减为指向其第一个元素的指针。所以
int*q=arr
使用数组第一个元素的地址初始化int指针
q
:一切正常,不发出警告

但是
&arr
是数组的地址。它只能正确用于初始化(或分配)指向数组或相同大小的指针,或指向大小不确定的数组的指针。您可以使用它初始化指向int的指针(int是一种不同且不兼容的类型),编译器会就此发出警告。因为根据标准,使用从指向不同类型的指针初始化的指针是未定义的行为

但在常见的实现中,指向任何类型的指针都具有相同的表示形式,即对象第一个字节的地址。因此,即使标准不允许,指令
int*p=arr以与
p
相同的值结束,这将给出正确的
int*p=arr。这就解释了为什么你的程序仍然给出了期望值


顺便说一句,未定义的行为并不禁止预期的结果,只是不同的编译器可能会给出不同的结果、崩溃、过早结束而没有错误、踢你的狗等等(即使到目前为止还没有编译器可以打我的狗;-)

数组类型与(;但不等同于)指向数组项的指针兼容,因此,在第二种情况下没有问题。编译带有
gcc-std=c11-pedantic错误的C代码是一种很好的做法,尤其是在学习时。这将阻止您执行无效的C代码。前两个示例都没有指向数组,而是指向数组的第一项(第二个是直接获取其地址,第一个是由于数组衰减)。在许多情况下都会吹毛求疵,但可能与问题的上下文有关。不,
&arr
给出的结果类型为
int(*)[5]
。这是有道理的。谢谢
 int *q = &arr[0];   // RHS == LHS
 int *q = &arr; // LHS (int *) != RHS (int (*) [5])