为什么这两个是相同的东西,但我看到了一个类似问题的答案,但却无法真正理解。
为什么*(a+i)
和a+i
做同样的工作。
int a[1][2] = {1,2};
printf("%p or %p\n",*(a+0),a+0);
答案 0 :(得分:1)
根据指针操作,定义了数组下标括号“ []”。假设a为数组类型且i为整数类型,则将a [i]定义为*(a + i)。数组的名称本身被评估为指向其第一个元素的指针。指针的整数加法将整数乘以元素大小到指针。 (a + i)和*(a + 1)不同--(a + i)是数组中元素的地址,而*(a + i)是该元素。
因此a [0]变为*(a + 0)或* a,即数组的第一个元素。 A [1]变为*(a + 1)或*(a +(sizeof(* a)* 1),或*(数组第二个元素的地址),或者只是数组第二个元素,例如人们会期望的。
答案 1 :(得分:1)
除非它是sizeof
或一元&
运算符的操作数,或者是用于初始化声明中的字符数组的字符串文字,否则 expression 为类型“ T
的N元素数组被转换(“衰减”)为类型为“ T
的指针”的表达式,该表达式的值是该元素的第一个元素的地址数组。
因此给出了类似的声明
int a[100];
任何时候a
都不是sizeof
或一元&
运算符的操作数的表达式中,编译器会将其视为等同于{{ 1}}(输入&a[0]
)。
现在,如果int *
是一元a
的操作数怎么办?
数组的地址与数组的第一个元素的地址相同,应该从下图中清楚地看到:
&
表达式 +------+
a: | a[0] |
+------+
| a[1] |
+------+
...
+------+
| a[99]|
+------+
的类型是&a
(指向int (*)[100]
的100个元素的数组的指针),但是 value 与int
和a
(第一个元素的地址)相同。
总结在表中:
&a[0]
同样,数组的地址与数组的第一个元素的地址相同,因此int a[100];
Expression Type "Decays" to
---------- ---- -----------
a int [100] int *
*a int n/a
&a int (*)[100] n/a
a[i] int n/a
&a[i] int * n/a
,&a
和a
将产生相同的地址值(对任何类型的转换进行模运算)。
将1加到指针将产生指向类型的下一个对象的地址。 IOW,如果&a[0]
指向一个4字节的p
对象,则int
的结果将是下一个4字节p + 1
的地址。如果int
指向4字节p
的100个元素的数组,则int
会产生{{的下一个100个元素的数组1}}。
下标操作p + 1
定义为int
-给定地址a[i]
,找到*(a + i)
之后的第a
个对象的地址并取消引用结果。
这意味着i
的值与a
-*a
相同。
这在您的示例中如何应用于2D阵列?
给出声明
a[0]
表达式 *a == *(a + 0) == a[0]
“衰减”从类型“ {{1}的2元素数组的1元素数组”(int a[1][2];
)到“指向a
“(int
)的2元素数组。表达式int [1][2]
的值是第一个元素的地址,但是第一个元素本身具有数组类型。因此,该值“衰减”到指向子数组第一个元素的指针。
这是一个方便的表格总结:
int
同样,int (*)[2]
,a
,int a[1][2];
Expression Type "Decays" to
---------- ---- -----------
a int [1][2] int (*)[2]
*a int [2] int *
&a int (*)[1][2] n/a
a[0] int [2] int *
*a[0] int n/a
&a[0] int (*)[2] n/a
a[0][0] int n/a
,&a
和a
将产生相同的值,因为{{1}的地址}与a[0]
的地址相同,也与&a[0]
的地址相同。
答案 2 :(得分:0)
一个数组可以衰减指向其第一个元素的指针。在您的示例中,平原a
将衰减到&a[0]
。对于 any 数组或指针a
和索引i
,表达式a[i]
精确地等于*(a + i)
。
此外,如果您将数组布置成在内存中的样子,则应该是
+---------+---------+ | a[0][0] | a[0][1] | +---------+---------+
现在有了这些信息,我们就可以开始在您的printf
调用中转换表达式了。
让我们以*(a + 0)
开头:
*(a + 0)
变为a[0]
。a[0]
是一个数组,它将衰减为指向其第一个元素即&a[0][0]
的指针。因此第一个参数等于&a[0][0]
,即指向a[0][0]
的指针。
然后让我们拿a + 0
。
a + 0
等于&*(a + 0)
。&*(a + 0)
变为&a[0]
。 &a[0]
是指向a
的第一个元素的指针,该元素恰好与a[0][0]
在内存中的同一位置开始。
这当然意味着指针&a[0]
和&a[0][0]
都指向相同的位置,并且输出将相等。 但是 这两个指针的类型非常不同:
&a[0][0]
的类型是指向int
的指针,即int *
&a[0]
的类型是指向两个int
元素(即int (*)[2]
答案 3 :(得分:0)
C并没有真正将多维数组与一维数组区别对待。 C语言中的数组只是数组的数组
char a[2][3][4][5];
是array 2 of array 3 of array 4 of array 5 of char。
解引用/下标对任何“ T的数组A”都相同:
sizeof(T)
缩放在C语言中使用解引用/下标,当您谈到一个时,您就说了另一个,因为A[Index]
或Index[A]
被定义为与*(A+Index)
或{{1 }}。
后缀表达式后跟方括号[]是数组对象元素的下标名称。下标运算符[]的定义是E1 [E2]与(*((E1)+(E2)))相同。由于适用于二进制+运算符的转换规则,如果E1是一个数组对象(相当于一个指向数组对象的初始元素的指针),而E2是一个整数,则E1 [E2]指定E1的第E2个元素E1(从零开始计数)。
由于*(Index+A)
中的char a[2][3][4][5];
是数组2的(字符3的数组4的数组3的数组3),a
会给你{{ 1}},结果将为a[1]
类型。
现在这是数组的特殊地方-数组在C中不是一流的对象。您不能拥有数组类型的r值。当您尝试获取一个数组(例如,通过将数组传递给函数或运算符)时,数组会立即衰减为指向其第一个元素的指针,因此((char*)&a) + 1 * sizeof(char[3][4][5])
的{{1}}类型会立即变为{{ 1}}。
成功的下标运算符指定多维数组对象的元素。如果E是尺寸为i x j x的n维数组(n> = 2)。 。 。 x k,然后将E(用作左值)转换为指向维度为j x的(n-1)维数组的指针。 。 。则如果将一元*运算符显式或间接地应用于下标,则结果是被引用的(n-1)维数组,如果用作左值以外的数组,则其本身将转换为指针。因此,数组以行优先顺序存储(最后一个下标变化最快)。
此操作将递归进行,直到您切掉所有尺寸(从右到左),并剩下不衰减的真实类型为止。实际上,中间数组的衰减意味着中间的反引用/下标实际上并没有获取任何东西,它们只是对基地址的添加。
带有char[3][4][5]
的一些示例:
char[3][4][5]
应用于您的示例:
a[1]
由于char(*)[4][5]
中的deref尚未达到真实类型,因此没有访存,只是对基本指针的补充
与类型调整。由于加法器加了0,因此该值没有改变,与char a[2][3][4][5];
衰减到指向其第一个元素(== {#include <stdio.h>
char a[2][3][4][5];
#define ASSERT_TP(Expr,Tp) _Generic(Expr,Tp: (char*)(Expr))
int main()
{
printf("%zd\n", ASSERT_TP(a,char(*)[3][4][5])
- (char*)a); //0
printf("%zd\n", ASSERT_TP(a[1],char(*)[4][5])
- (char*)a); //60 == 1 * (3*4*5)
printf("%zd\n", ASSERT_TP(a[1][1],char(*)[5])
- (char*)a); //80 == 1 * (3*4*5) + 1 * (4*5)
}
)甚至是int a[1][2] = {1,2}; // a decays to ptr to 1st element,
//i.e. to `int (*a)[2]`
printf("%p or %p\n",
*(a+0), // == a[0]; ((char*)&a) + 0*sizeof(int[2]);
// type is int[2], which decays to int*
a+0); // == a (after decay); (char*)&a + 0*sizeof(int[2]);
//type is still `int(*)[2]` (because no derefing)
(将具有相同的数字地址,但其类型为*(a+0)
)。