C---指针数组 & 数组指针---图解篇

0.指针数组 & 数组指针(相见恨晚篇)

《C语言深度剖析》上面的解析如下:

指针数组: 首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。

数组指针: 首先它是一个指针,它指向一个数组。在32 位系统下任何类型的指针永远是占4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。

下面到底哪个是数组指针,哪个是指针数组呢:
A) int *p1[10];
B) int (*p2)[10];

  • “[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。

  • 至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。

我们可以借助下面的图加深理解:

在这里插入图片描述

这里有个有意思的话题值得探讨一下:
平时我们定义指针不都是在数据类型后面加上指针变量名么?这个指针p2 的定义怎么不是按照这个语法来定义的呢?也许我们应该这样来定义p2:
int ()[10] p2;
int (
) [10] 是指针类型,p2 是指针变量。这样看起来的确不错,不过就是样子有些别扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p2 前移了而已。你私下完全可以这么理解这点。虽然编译器不这么想。_



聪明的你,看完上面的讲解好像都理解了,是吧?

如上的解释,我再把重点啰嗦几句:

  • 指针数组:本质是数组,只是里面存放的是多个指针(int * p1[10])。此处的p1是数组名,指针名没有。如果想获取指针&p1[i],这样可以获取指针
  • 数组指针:本质是指针,虽然初始化的时候指向了一个数组( int(*p2)[10] ),但其实只是一个指针。p2是指针变量,想通过指针获取地址,可以用指针的偏移去获取,例如p2 + 1,代表第二个元素的地址
  • 有关指针的偏移,可以参考博文:https://blog.csdn.net/chenmozhe22/article/details/106410963(图文并茂)

1.指针数组

#include <stdio.h>


int main() {

	int i;
	char* pch[5] = { "我", "爱", "你", "祖", "国" };		// 此处初始化只能是5个元素,多一个少一个都不行
	printf("%s  \n", pch[0]);			// pch是数组名,所以通过索引直接可以获取到元素内的每个值
	printf("%s  \n", *(&pch[0]));		// 既然每个变量可以找到,就能通过* + & + 变量方式,对指针取值

	*(&pch[3]) = "中";					//pch[3] = "中";方式也一样效果
	for (i = 0; i < 5; i++) {
		printf("%s, ", pch[i]);
	};
	
	printf("\n");
	printf("%s\n", *(&pch[1] + 1));		// 通过指针获取其他元素
	printf("%d \n", sizeof(pch));		// 因为pch是指针数组,里面存放的是5个指针,所以4*5 = 20个字节

	return 0;
};


我
我
我,,,,,20

2.数组指针

#include <stdio.h>


int main() {

	int i;
	int list[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

	int* p1 = &list;					// 指针指向数组方式一

	printf("%p  \n", p1);
	printf("%p  \n", p1+1);				// &list[0] + 1
	printf("%d  \n", *p1);
	printf("%d  \n", *p1 + 1);

	printf("===================\n");

	int(*p2)[10] = &list;				// 数据指针:指向了某个数组的指针,方式二
	printf("%p  \n", p2);				//p2 和 *p2 的地址是一样的???
	printf("%p  \n", *p2);
	printf("%d   \n", **p2);			// **p2是元素的实际值
	printf("%p  \n", *p2 + 1);
	printf("%p  \n", (*p2 + 2));


	return 0;
};


012FF6B8
012FF6BC
1
2
===================
012FF6B8
012FF6B8
1
012FF6BC
012FF6C0

如上结果,我主要困惑的地方如下:
1.我们看到p1 & p2 & *p2的值是一样的
2.**p2才是具体的值,那p2算是指针的指针吗?

3.再谈二维数组

有关二维数组的概念和基础,可参考:https://blog.csdn.net/chenmozhe22/article/details/106280578

1.二维数组中的指针 & 数组名

#include <stdio.h>

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

	printf("%p   \n", p);
	printf("%p   \n", *p);
	printf("%d   \n", **p);
	printf("=================\n");
	printf("%p   \n", a);
	printf("%p   \n", *a);
	printf("%d   \n", **a);

	return 0;
}


00D3FD94
00D3FD94
0
=================
00D3FD94
00D3FD94
0


在这里插入图片描述

根据代码,发现:

  • **p / **a才能得到,二维数组元素对应的值
    -在一维数组中(*p可以得到最终的元素),p代表的是指针变量;那么二维数组(**p可以得到最终的元素),那 *p 就是指针变量。
  • p则是真实指向二维数组,首元素的指针,这里有个概念,p不是指针的指针,可参考:https://blog.csdn.net/toyalll/article/details/50043193
  • 我们可以通过指针、地址两种方式,分别获取并修改二维数组中的值。

2.获取二维数组中元素的值–指针法


#include <stdio.h>

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

	printf("%d   \n", **p);		
	printf("%d   \n", *(*p + 1));		// *p+1,可以得到下一个元素的指针地址
	printf("%d   \n", *(*p + 2));		
	printf("=================\n");
	printf("%d   \n", *(*(p + 1)));		//*(p+1),可以得到下一行元素的指针地址
	printf("%d   \n", *(*(p + 1)+1));
	printf("=================\n");
	printf("%d   \n", *(*(p + 2)));
	printf("%d   \n", *(*(p + 2)+1));

	return 0;
}

有关指针的偏移,可以参考:
https://blog.csdn.net/chenmozhe22/article/details/106410963

0
1
2
=================
4
5
=================
8
9

如上代码,经过优化之后如下:

#include <stdio.h>

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

	for (i = 0; i < 3; i++) {
		for (j = 0; j < 4; j++)
			printf("%2d  ", *(*(p + i) + j));
		printf("\n");
	}

	return 0;
}


 0   1   2   3
 4   5   6   7
 8   9  10  11

3.获取二维数组中元素的值–地址法

int main() {

	char list[2][5] = { { '1', '2', '3', '4', '5' }, { 'a', 'b', 'c', 'd', 'e' }};

	printf("%c  \n", list[0][0]);		//获取第一行的第一个元素
	printf("%c  \n", list[1][0]);		//获取第二行的第一个元素
	printf("%p    \n", &list[0]);		
	printf("%p    \n", &list[1]);
	printf("%d-%d",&list[1],&list[0]);	// 计算第一行第一个元素和第二行第一个元素相差的字节数
	return 0;	
};


1
a
00DBFD3C
00DBFD41
14417217-14417212

#include <stdio.h>

int main() {

	char list[2][5] = { { '1', '2', '3', '4', '5' }, { '6', '7', '8', '9', '0' } };

	for (int i = 0; i < 2; i++) {
		for (int j = 0; j < 5; j++)
			printf("%c  \t", *(&list[i][j]));
		printf("\n");
	};

	return 0;
};



1       2       3       4       5
6       7       8       9       0






文章参考:

  • 《C语言深度剖析》(强烈推荐大家看看,特别是初学者,昨天买了一本,家中常备!)
  • https://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html
  • https://www.cnblogs.com/mq0036/p/3382732.html#commentform
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章