C/C++ 由int (*p)[5]与int *p[5]引发的学习与思考

由int (*p)[5]与int *p[5]引发的学习与思考

  • 最近在大一上复习指针这部分时,有个C指针的特例一直困扰着我。我查阅了许多课本,也看了CSDN上许多大佬的理解。自我感觉受益匪浅,现记录下自己现阶段对这一类问题的学习和思考,以防遗忘。

  • 大佬Joerrot的博客:对行列指针关系的理论阐述
    大佬JZJZ73的博客:举遍历二维数组的例子体现行列指针的不同

  • 【1】int (*p)[5]

  • 本质: 首先分析一下,因为()的优先级大于[ ],所以这个表达式其实是在定义一个指针,本质是*p,而[5]在(*p)后面作用是修饰这个指针,补充说明这个指针指向的是一个包含5个元素的数组。

  • 在二维数组中的含义: 如果把这个指针指向的那个一维数组想像成二维数组的一行,那么这个指针其实就是所谓的行指针。

    # 行指针的定义:指向某一行,不指向具体的元素。

  • 例如一个二维数组a[5][4],那么p其实就是a,即第一行的地址(也可以说p就是&a[0],后面我会给大家讲到),同理p+1就是a+1……行指针在二维数组中是很常用的,特别是在对二维数组进行行操作时候能显示出它的便捷。说到行指针不可避免的要说到列指针,其实在二维数组中行指针指向的对象就是列指针。

    # 列指针的定义:指向行中具体的元素。

  • 由列指针的定义可以看出列指针其实很简单,就是一个指向数组中单个元素的指针,具体有关行指针和列指针关系问题,在上面大佬的两篇博客中也介绍了它们的关系:从列指针的角度看,只要在同一行,不管它们指向行中的哪个元素,它们的行地址都是在同一行的地址,所以它们的行地址都是一样的。同样我们得出结论:&列指针->行指针。 相反地可以推出:*行指针->列指针。 我最开始的理解就是把行指针看成是指向列指针的指针,这么想我当初认为一点错都没有,比如定义一个二维数组a[4][5],那么a就是行指针,*a是列指针,而常规的理解就是 *a是a所指向对象的内容。同理对于a[0]这一列指针来说,&a[0]就是行指针。具体表现形式如下:
    对于关系式常规的理解

  • 这么想是不是一点问题都没有?
    但如果行指针是指向指针的指针(二级指针)的话,那么行指针a与它所指向的指针(列指针) * a应该是不一样的,因为指针指向那块内存的地址肯定不等于指针指向的那块内存表示的内容。但事实上在二维数组中a与* a确实是相等的,下面是我在VS中test的截图:
    源代码
    运行结果
    可以看出a与*a所表示的内容是一样的,那么说明二维数组其实并不是一个二级指针。其实想想也不对嘛,行指针+1后指针跨过的是一个一维数组的长度,比如int (*p)[5],那么p每次+1跨过的都是一个存有5个int型元素的一维数组。如果int a[5],那么跨过的就是sizeof(a)。但是二级指针因为是指向指针的指针,如果+1,仅仅跨过一个指针的大小(一般电脑中一个指针的大小默认是4个字节)。 所以只能说行指针等同于一个二级指针,但不是真正的二维指针。

  • 那么到底行指针与列指针关系图是怎样的呢?我们知道二维数组在栈中的存放形式与一维数组一样都是一个接一个存放的,并没有像矩阵这样存放。但是为了方便理解我们一般都会把它想象成一个矩阵的形式。以下这张图是我在谭浩强的《C程序设计(第五版)》中拍下来的,有这本书的读者也看以看一下书P244-245页对于这方面内容的介绍。
    行列指针关系图

  • 注意 这下我们来解决上述问题a与* a相同的问题。首先我们知道了行指针int (* p)[5]与二级指针有着本质差别,所以不能按照常规二级指针的方式来理解它。(行指针是C语言里面的两大奇葩之一)那么我们应该怎样理解呢?对于二维数组a[m][n],a是二维数组的数组名,就是二维数组的首地址,即a[0][0]。(a也是行指针,表示的是二维数组第一行的首地址,也就是&a[0][0]) 而* a是列指针,指向元素a[0][0],那么* a自身的内容就是&a[0][0]。所以这两个是相等的。其实它们只是纯内存相同,但基类型是不同的。

  • 值得一提的是我发现a[0]有两种理解方式,一种可以理解为a[0]+0,也可以理解成a+0。 这两种理解方式一个是按行指针来理解,另一个是按列指针来理解。大家有什么想法吗?欢迎各位读者评论指点。

  • 个人认为指针与二维数组的关系其实并不算太难,只要注意下行指针int (* p)[n]是一个奇葩的指针定义形式就行,指向的是一个包含n个int型变量的数组。它与二级指针有着本质的差别。所以a与* a不能按照常规来理解。

  • 【2】int *p[5]

  • 本质: 这个定义相比于行指针就容易理解多了,首先p先与[5]结合,可以很容易判断出这是在定义一个数组,而前面的int * 则表示这个数组存放的元素都是int * 类型的,即每个元素都是一个指针。例如char *friends[5]={“smith”,“jackey”,“rose”,“marli”,“hanmeimei”}就表示这是一个包含5个字符指针的字符数组,每个字符指针都指向一个字符串。个人认为这类定义其实就可以看成一个二维字符数组去解决,即char friends[ ][5]={“smith”,“jackey”,“rose”,“marli”,“hanmeimei”}。

  • 实战 大家可以试一下这道题,我觉得很有意义。
    题目: 对char * friends[5]={“smith”,“jackey”,“rose”,“marli”,“hanmeimei”};进行查找某一个好朋友如"rose",如果找到删除它,没找到提示没有找到。

  • 以下为本题代码,因为VS中认为上述赋值为常量赋值,必须得要定义成const char * 所以我简单在Dev-c++5.11上写了一下此题的代码:

#include <stdio.h>
#include <string.h>
int search(char *p[5],char *);
int main()
{
	char *friends[5] = { "smith","jackey","rose","marli","hanmeimei"};
	char a[20];
	scanf("%s", a);
	int order;
	if ((order = search(friends, a)) == -1)
		printf("没有找到");
	else
	{
		for (int i = order; i < 4; i++)
		{
			friends[i]=friends[i + 1];
		}
		for (int i = 0; i < 4; i++)
		{
			printf("%s\t", friends[i]);
		}
	}
}

int search(char *p[5],char *a)
{
	for (int i = 0; i < 5; i++)
	{
		if (!strcmp(p[i], a))
		{
			return i;
		}
	}
	return -1;
}
  • 本博客内容皆为本人学习思考过程,因本人水平有限,如有不足之处欢迎指正。
    本人联系方式(QQ):1666271035
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值