int (*ptr)[4] 的真正含义是什么,它与 *ptr 有何不同?

What int (*ptr)[4] really means and how is it different than *ptr?

int (*p)[4] , *ptr;
int a[4] = {10,20,30,40};
printf("%p\n%p\n%p",&a,a,&a[0]);
p = &a ;
//p=a;        gives error

//ptr = &a;   gives error
 ptr = a;

输出:

0x7ffd69f14710
0x7ffd69f14710
0x7ffd69f14710

我试图了解什么是a&a&a[0] returns及其起始变量的内存地址。那么,为什么我在其中一些作业中会出错?

我的意思是,如果 p = &a = 0x7ff... 有效,为什么 p = a = 0x7ff.. 不行?

如果可能的话,任何人都可以通过框图让我理解这个 p 和 ptr 实际指向的位置。或者他们只是指向相同。但它们是不同的东西,我肯定知道。

p 是指向类型 int[4] 的值的指针,即指向每个 4 个整数的数组的指针。请注意 sizeof(*p)sizeof(int).

的 4 倍

现在,

  • p = a 失败,因为 a 在分配时衰减为指向 int 的指针,而 p 指向不同的类型。阅读更多关于衰减的信息:What is array to pointer decay?
  • ptr = &a 失败,因为 ptr 实际上是一个指向 int 的指针;它的类型与 p 不同。在同一行上声明多个变量通常会造成混淆,因为并非所有语法都适用于您声明的 everything;最好将这些定义拆分成单独的行。

I tried to understand what a, &a, and &a[0] are.

在 C 中,数组会退化为指针。所有这些指针都引用相同的内存位置(数组的第一个元素)。唯一的区别是类型。

a&a[0] 具有数组元素的类型(在本例中为 int

&a 是指向元素数组类型的指针类型(在本例中为 4 个整数的数组)。

假设指针是激光笔,具有不同的颜色(红色表示指向 int 的指针,绿色表示指向数组的指针,...)变量是您可以使用正确的激光笔指向的东西,即不能用绿色激光笔指向char变量。

好的,所以你有 int a[4] 一个数组(4 个整数)。使用绿色指针指向它:int (*green)[4] = &a;...您还有一个 int (a[0]),您可以使用红色指针指向它:int *red = &a[0]; /* in most contexts 'a' by itself is converted to "address of first element": &a[0] is the same as a */.

现在问问你的色盲朋友指针指向哪里:)
对你的朋友来说,他们是平等的,指向同一个"place"...但是你骗了你的朋友! 编译器是色盲的,不喜欢被欺骗

"What int (*ptr)[4] really means and how is it different than *ptr?"

首先,我们看一下声明本身:

int * ptr - ptr 属于 int * 类型 - 指向 int.

的指针

int (*ptr)[4] - ptrint (*)[4] 类型 - 指向四个数组的指针 int.

类型不同


I mean, if p = &a = 0x7ff... works, why not p = a = 0x7ff..?

(挑剔的旁注:这些表达式不会被编译,但我知道这只是一个说明上下文的示例。)

在 C 中,数组类型的表达式能够退化为指向数组第一个元素的指针。

引自 C18 标准,ISO/IEC 9899:2018:

"Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined."

Source: C18, §6.3.2.1/3

但是由于 & 运算符用于 a,因此 a 不会衰减为指向 a 的第一个元素的指针。相反,& 应用于数组本身并生成指向整个数组的指针(类型 int (*)[4])。

是句法类型difference/mismatch,int * vs. int (*)[4],当然两者都指向内存中的同一个地址。

编译器有义务对任何类型不匹配进行诊断,因为它是语法违规。

当然,两者具有相同的地址,但赋值时的类型不兼容造成了差异。

这是不同类型的问题,类型是编译器中存在的概念,但编译后的二进制文件中没有。这就是为什么即使两种指针类型实际上指向同一地址也会出现编译器错误。

您可以将 int (*p)[4]=&arr; 视为 指向整个数组的指针 ,而 int* ptr=arr; 指向第一个元素的指针在数组中。

通常在表达式中使用时,数组名"decays" 变成指向第一个元素的指针。这就是我们写 int* ptr=arr; 时发生的情况——它 100% 等同于写 int* ptr = &arr[0];.

"array decay" 规则在 C17 6.3.2.1/3 中正式定义:

Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.

正如我们所见,& 运算符是规则的一个特殊例外。这意味着在 &arr 的情况下, arr 部分不会衰减。所以我们应该得到一个指向数组类型的指针,而不仅仅是第一个元素。这就是 int (*p)[4] 的用武之地。

当然,"a pointer to the whole array" 会同时指向第一项的地址,因为那是数组开始的地址。如果我们printf("%p\n", p),无论我们传递数组指针还是指向第一个元素的指针,我们都会得到相同的地址。

数组指针用于保持语言类型系统的一致性。当我们开始使用多维数组时,我们有时 运行 实际上也会进入它们。例如,如果我们定义一个 int arr[2][3] 数组,它实际上是一个包含 2 个项目的数组,其中每个项目都是一个 int[3] 数组。那么当我们为这个二维数组键入 arr 时会发生什么?像往常一样,数组衰减为指向第一项的指针。第一项是一个数组,所以为了使数组衰减的规则保持一致,它必须给出一个指向这样一个包含 3 个整数的数组的指针。这种指针的类型是 int(*)[3]

通过这些讨论,两个答案都得到了明确。我想总结一下:

1。 int *ptr 和 int *ptr[4];

有什么区别 答案 : 两个指针变量的大小相同,因为它们都只保存地址。 ptr 保存整数的地址只是概念上的区别。嗯,当然你可以用它来指向数组的起始位置。但这说明编译器是:它可以容纳任何整数。当您尝试在代码中执行 "ptr++" 时,它只会将内存地址提前 1 个单位(根据为该系统的整数保留的任何字节)。但是, int *ptr[4] 说,ptr 是一个指向整个数组的指针,只存储了起始位置。好吧,在这两种情况下,当然 ptr 存储相同的地址。但是当你尝试在这种情况下执行 "ptr++" 时,它会向前移动 4 个单位,因为编译器将其解释为数组指针,而不是整数指针。

2。为什么 ptr=&a 有效而 ptr =&a[0] 或 ptr=a 无效,即使这些所有值都相同?

答案 : ptr=a 和 ptr=&a 在概念上都是正确的。但是,编译器遵循严格的规则。如果你想说 ptr 包含一个整数的地址,那么它应该被分配 ptr = a OR ptr=&a[0] (表示分配的 space 是一个整数)。然而,如果 ptr 被声明为一个数组的地址, ptr = &a[0] pr ptr = a 被编译器解释为这个 ptr 获取一个整数的地址,这是不正确的,因为这里的 ptr 表示一个数组的地址大批。在这种情况下,它不应包含整数地址。因此,p=&a 在编译器看来在句法上非常正确。因此,这是它接受的唯一选择。

:)

基本规则如下:

对于任何1类型T,您可以有以下任何一项:

T *p;        // p is a pointer to T
T *a[N];     // a is an array of pointer to T
T (*a)[N];   // a is a pointer to an array of T
T *f();      // f is a function returning pointer to T
T (*f)();    // f is a pointer to a function returning T

后缀 []() 运算符的优先级高于一元运算符 *,因此像 *p[i] 这样的表达式被解析为 *(p[i])。如果要索引到 p 指向 的内容,则需要将 * 运算符与 p 或 [=23= 显式分组].此优先规则适用于表达式和声明。

除非它是 sizeof_Alignof 或一元 & 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则类型 "N-element array of T" 的 expression 将被转换 ("decay") 为类型 "pointer to T" 的表达式并且表达式的值将是数组的第一个元素。所以给出声明

int a[4] = {10, 20, 30, 40};

以下所有都是正确的:

Expression        Type            Decays to             Equivalent value
----------        ----            ---------             ----------------
         a        int [4]         int *                 &a[0]
        &a        int (*)[4]      n/a                   &a[0]
        *a        int             n/a                    a[0]

表达式 a 的类型为“int 的 4 元素数组”(int [4])。 a 不是 sizeof_Alignof 或一元 & 运算符的操作数,因此表达式 "decays" 键入 "pointer to int" 和值表达式的是第一个元素的地址。该表达式的结果完全等同于 &a[0]

表达式 &a 的类型为 "pointer to 4-element array of int" (int (*)[4])。在这种情况下,a 一元运算符 & 的操作数,因此衰减规则不适用。

所有表达式 a&a&a[0] 产生相同的 value([的第一个元素的地址=29=]),但是表达式的 types 不同(这可能会影响值的表示方式)。 a&a[0]的类型是int *,但是&a的类型是int (*)[4]

类型对于指针运算之类的事情很重要。假定以下声明:

int a[4] = {0, 1, 2, 3};
int *p = a;
int (*ap)[4] = &a;

pap 最初都指向同一个地址。但是,表达式 p + 1 将产生下一个 int 对象的地址,紧随 p 指向的对象 (IOW, &a[1] ),而 ap + 1 将生成 a 之后 int 的下一个 4 元素数组的地址。

这正是数组下标的工作原理 - 表达式 a[i] 被计算为 *(a + i)。给定一个起始地址 a,偏移 i 个对象( 不是字节 )并取消引用结果。

这就是您在某些作业中出现错误的原因 - int *int (*)[4] 类型不 兼容 。一方面,它们不必以相同的方式表示(尽管在您可能使用的任何系统上它们都会是),并且在使用指针算法时它们的行为不同。

指向不同类型的指针本身就是不同的类型,通常不能互换。


  1. 嗯,几乎任何函数都不能 return 数组或函数类型,并且你不能有函数类型的数组,所以对于 T (*a)[N] T 不能是函数类型,对于 T (*f)() T 不能是函数或数组类型。