2d数组和指针有何关系?

时间:2019-02-12 18:10:00

标签: c arrays pointers multidimensional-array

为什么这两个是相同的东西,但我看到了一个类似问题的答案,但却无法真正理解。

为什么*(a+i)a+i做同样的工作。

int a[1][2] = {1,2};
printf("%p or %p\n",*(a+0),a+0);   

4 个答案:

答案 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 与inta(第一个元素的地址)相同。

总结在表中:

&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 &aa将产生相同的地址值(对任何类型的转换进行模运算)。

将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]aint 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 &aa将产生相同的值,因为{{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)开头:

  1. 由于等价,*(a + 0)变为a[0]
  2. 由于a[0]是一个数组,它将衰减为指向其第一个元素即&a[0][0]的指针。

因此第一个参数等于&a[0][0],即指向a[0][0]的指针。

然后让我们拿a + 0

  1. 表达式a + 0等于&*(a + 0)
  2. 因为数组/指针等效项&*(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”都相同:

  • 将A衰减到第一个元素的地址(如果要延迟/订阅指针,则不执行任何操作)
  • 此外,索引按sizeof(T)缩放

在C语言中使用解引用/下标,当您谈到一个时,您就说了另一个,因为A[Index]Index[A]被定义为与*(A+Index)或{{1 }}。

6.5.2.1p2

  

后缀表达式后跟方括号[]是数组对象元素的下标名称。下标运算符[]的定义是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}}。

6.5.2.1p3

  

成功的下标运算符指定多维数组对象的元素。如果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))。