C语言二维数组指针用法

目录

题目

背景概念梳理

一维线性

数组指针

指针步长

数组名与指向其的指针变量名等价

数组的初始化与取元素

数组指针转换关系

解题过程

选项A:* (( * prt+1)[2])

选项B:* ( * (p+5))

 选项C:( * prt+1)+2

选项D: * ( * (a+1)+2)

正确答案

整体代码

参考链接


题目

设有以下定义:

int a[4][3] = {1,2,3,4,5,6,7,8,9,10,11,12};
int (*prt)[3] = a, *p = a[0];

则以下能够正确表示数组元素a[1][2]的表达式是()

A. * (( * prt+1)[2])
B. * ( * (p+5))
C. ( * prt+1)+2
D. * ( * (a+1)+2)

链接:设有以下定义: a[4][3]={1,2,3,4,5,6__牛客网,来源:牛客网

背景概念梳理

        以本题为例,二维数组a[4][3],所表示的元素排列如下:

[[ 1, 2, 3],
 [ 4, 5, 6],
 [ 7, 8, 9],
 [10,11,12]]

        a[4][3]可理解为“一个四行三列的数组”,亦可理解为“数组a包含4个数组元素,每个数组元素包含三个整形元素”

一维线性

        内存中,a 的分布是一维线性的,整个数组占用一块连续的内存,C语言中的二维数组是按行排列的,也就是先存放 a[0] 行,再存放 a[1] 行,最后存放 a[2] 行;每行中的 3 个元素也是依次存放。数组 a 为 int 类型,每个元素占用 4 个字节,整个数组共占用 4×(3×4) = 48 个字节。

        C语言允许把一个二维数组分解成多个一维数组来处理。对于数组 a,它可以分解成三个一维数组,即 a[0]、a[1]、a[2]、a[3]。每一个一维数组又包含了 43个元素,例如 a[0] 包含 a[0][0]、a[0][1]、a[0][2]。

数组指针

int (*prt)[3] = a;

        括号中的*表明 prt 是一个指针变量,它指向某数组,这个数组的类型为int [3](即这个数组是一个包含三个元素的数组),这正是 a 所包含的每个一维数组的类型。指针prt指向a的首个一维数组。

指针步长

        prt是一个指向数组的指针,指针的步长取决于其指向的数据变量的大小,prt指向的是一个数组类型为int[3]的数组,那么其数据结构大小为(3*4)=12字节,即prt在当前情况下,步长为12,(prt+1),(prt-1),所指向的数据内存地址,各差12。

数组名与指向其的指针变量名等价

        数组名表示数组的第一个元素的地址,可将其看作是一个指针常量。而这个指针常量所指向的类型与数组元素的类型一致。在本题中,prt作为指向数组a的第一个元素的地址的指针变量名,数组a的数组名"a"亦表示其数组的第一个元素的地址,两个都是指向数组a的第一个元素的地址,故数组名 a 在表达式中也会被转换为和 prt 等价的指针。

数组的初始化与取元素

        以一维数组为例,初始化的时候"[]"中,所填的数值是要给数组分配几个元素的位置;取元素时,“[]”中,所填的数值是要获取那个索引值的元素,数组的索引值都是从零开始的。“[]”中所填的数值,在数组初始化和取元素时所表达的含义完全不一样。

//初始化
int num[5]; //数组 num 中有 5 个元素,每个元素都是 int 型变量,而且它们在内存中的地址是连续分配的 

//取元素
int n1 = num[0];//获取num数组的第零个元素
int n2 = num[1];//获取num数组的第一个元素
int n3 = num[2];//获取num数组的第二个元素
int n4 = num[3];//获取num数组的第三个元素
int n5 = num[4];//获取num数组的第四个元素

数组指针转换关系

int a[4][3];
int (*p)[3] = a;

//0<=i<=3, 0<=j<=2 
a+i == p+i
a[i] == *(a+i) //a[i]等价于从数组a的首地址向后数第i个元素
a[i] == p[i] == *(a+i) == *(p+i) 
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)

解题过程

        依题,a[1][2]所对应的值是6,按选项逐个分析:

选项A:* (( * prt+1)[2])

*prt表示的是数组a第一个数组元素的地址=数组a的首地址,

(*prt+1)表示数组a中第二个整形元素的地址(在这里prt发生了退化,指向的是数组a中的首个数组元素的首个整形元素,实际运行结果,*prt与(*prt+1)差4字节,可佐证),

(*prt+1)[2]表示数组a第二个整形元素向后跨两个索引的值,

* (( * prt+1)[2])表示数组a第二个整形元素向后跨两个索引的值作为指针变量所指向的变量,

int main()
{
	int a[4][3] = {1,2,3,4,5,6,7,8,9,10,11,12};
	int (*prt)[3] = a, *p = a[0];
	
	printf("a[1][2]: %d\n",a[1][2]); //[]的作用 

	printf("\nA: *prt: %d\n",*prt);
	printf("A:(*prt+1): %d\n",(*prt+1));
	printf("A:(*prt+1)[2]: %d\n",(*prt+1)[2]);
	//printf("*((*prt+1)[2]): %d\n",*((*prt+1)[2])); //直接报错 
} 

        另外,做了一个关于指针退化的实验,*(prt+1)指向的是数组a的第二个数组元素(实验佐证,sizeof指针长度为12),但当再其基础上做加1处理后,其就退化成了指向第二行的首个数据的指针。

		
    printf("\nAtry: *(prt+1): %d\n",*(prt+1));
	printf("Atry: sizeof(*(prt+1)): %d\n",sizeof(*(prt+1))); //说明*(prt+1)获取的是第二行的数据
	printf("Atry: *(prt+1)+1: %d\n",*(prt+1)+1);//*(prt+1)退化成了第二行的第一个数据的地址, 
	printf("Atry: ((*(*(prt+1)+1)): %d\n",(*(*(prt+1)+1)));

        关于数组指针退化的问题,当直接对数组指针变量进行操作(eg. *(prt+1))后再解引用,都不会导致指针退化。除此之外,都会退化到指向某个整形元素。具体原因,*(prt+1)单独使用时表示的是第 1 行数据,放在表达式中会被转换为第 1 行数据的首地址,也就是第 1 行第 0 个元素的地址,因为使用整行数据没有实际的含义,编译器遇到这种情况都会转换为指向该行第 0 个元素的指针;就像一维数组的名字,在定义时或者和 sizeof、& 一起使用时才表示整个数组,出现在表达式中就会被转换为指向数组第 0 个元素的指针。

选项B:* ( * (p+5))

指针p直接指向的数组a的首个数组元素的首个整形元素,因为指针p的数据类型是int,

*(p+5)表示从数组a的首个数组元素的首个整形元素向后数的第5个整形元素的指针所指向的值,

*(*(p+5))表示从数组a的首个数组元素的首个整形元素向后数的第5个整形元素的指针所指向的值作为指针变量所指向的值(不存在嘛!直接报错)。

	//printf("*(*(p+5)): %d\n",*(*(p+5))); //直接报错 
	printf("\nB: (*(p+5)): %d\n",(*(p+5)));  
	printf("B: (p+5): %d\n",(p+5));

 

 选项C:( * prt+1)+2

*prt表示数组a的首个元素对应的地址

(*prt+1)表示数组a的第二个元素对应的地址

( * prt+1)+2表示数组a的第4个元素对应的地址

只有再在其基础上,再加一层解引用,才可以表示数组a的第4个元素对应的值

	printf("\nC: *prt: %d\n",*prt);
	printf("C: (*prt+1): %d\n",(*prt+1));
	printf("C: (*prt+1)+2: %d\n",(*prt+1)+2);
	printf("C: *((*prt+1)+2): %d\n",*((*prt+1)+2)); //只有再在其基础上,再加一层解引用,才可以表示数组a的第4个元素对应的值

 

选项D: * ( * (a+1)+2)

a等同于prt,

*(a+1)表示数组a的第二个数组元素的地址,

*(a+1)+2,发生指针退化,*(a+1)此时表示数组a的第二个数组元素中的首个整形元素的地址,整个表示数组a第二行的第二个元素的地址

* ( * (a+1)+2)表示数组a第二行的第二个元素的值

		
	printf("\nD: *(a+1): %d\n",*(a+1));
	printf("D: (*(a+1)+2): %d\n",(*(a+1)+2));
	printf("D: *(*(a+1)+2): %d\n",*(*(a+1)+2));
	
	printf("\nD: *(a): %d\n",*(a));
	printf("D: *(a+1): %d\n",*(a+1));
	printf("D: *(*a+1): %d\n",*(*a+1));

 

正确答案

选D,* ( * (a+1)+2)

整体代码

int main()
{
	int a[4][3] = {1,2,3,4,5,6,7,8,9,10,11,12};
	int (*prt)[3] = a, *p = a[0];
	
	printf("a[1][2]: %d\n",a[1][2]); //[]的作用 
	
	//printf("*((*prt+1)[2]): %d\n",*((*prt+1)[2]));
    printf("\nAtry: *(prt+1): %d\n",*(prt+1));
	printf("Atry: sizeof(*(prt+1)): %d\n",sizeof(*(prt+1))); //说明*(prt+1)获取的是第二行的数据
	printf("Atry: *(prt+1)+1: %d\n",*(prt+1)+1);//*(prt+1)退化成了第二行的第一个数据的地址, 
	printf("Atry: ((*(*(prt+1)+1)): %d\n",(*(*(prt+1)+1)));
		
	printf("\nA: *prt: %d\n",*prt);
	printf("A:(*prt+1): %d\n",(*prt+1));
	printf("A:(*prt+1)[2]: %d\n",(*prt+1)[2]);
	//printf("*((*prt+1)[2]): %d\n",*((*prt+1)[2])); //直接报错 
	
	
	//printf("*(*(p+5)): %d\n",*(*(p+5))); //直接报错 
	printf("\nB: (*(p+5)): %d\n",(*(p+5)));  
	printf("B: (p+5): %d\n",(p+5));
	
	
	printf("\nC: *prt: %d\n",*prt);
	printf("C: (*prt+1): %d\n",(*prt+1));
	printf("C: (*prt+1)+2: %d\n",(*prt+1)+2);
	printf("C: *((*prt+1)+2): %d\n",*((*prt+1)+2)); //只有再在其基础上,再加一层解引用,才可以表示数组a的第4个元素对应的值
	
		
	printf("\nD: *(a+1): %d\n",*(a+1));
	printf("D: (*(a+1)+2): %d\n",(*(a+1)+2));
	printf("D: *(*(a+1)+2): %d\n",*(*(a+1)+2));
	
	printf("\nD: *(a): %d\n",*(a));
	printf("D: *(a+1): %d\n",*(a+1));
	printf("D: *(*a+1): %d\n",*(*a+1));
} 

参考链接

数组的定义,初始化和使用,C语言数组详解数组可以说是目前为止讲到的第一个真正意义上存储数据的结构。虽然前面学习的变量也能存储数据,但变量所能存储的数据很有限。不仅如此,数组和指针(后续会讲)是相辅相成的http://c.biancheng.net/view/184.html

C语言二维数组指针(指向二维数组的指针)详解二维数组在概念上是二维的,有行和列,但在内存中所有的数组元素都是连续排列的,它们之间没有缝隙。以下面的二维数组 a 为例: int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; 从http://c.biancheng.net/view/2022.html

おすすめ

転載: blog.csdn.net/qq_17846375/article/details/121854776