指针与数组间的“恩恩怨怨”


最近在学习语法的时候有了不小的收获,对数组和指针这两个玩意有了一些新的认识,也发现自己以前的理解存在很大的问题,谨以此文梳理吾之所得

前言

指针就是指针,指针变量在64位系统下始终占8个byte,指针可以指向任何地方,但不是任何地方都可以通过指针变量访问的

数组就是数组,是相同类型变量组成的集合(就是数学中集合的意思),数组的大小由组成变量的类型和元素的个数决定,满足等式:数组大小=元素类型大小*元素个数

这二位是八竿子打不着,完全没有任何关系

为什么要强调二者没有任何关系呢?

因为他们经常“穿着相同的衣服”逗你玩,不信看下面代码

void Usart_Printf(u8 *str){
		
		u8 data=0;
		do{
	
// 		  USART_SendData(USART2,str[data]);//发送一个字
			USART2->DR = ( str[data]& (uint16_t)0x01FF);
		  while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待单字发送完成
			data++;
		}
		while(str[data]!=0);  //判断数据是否发送完成                                                  
		
	}

肯定会有人(以前的我)会认为str[data]为数组的一个元素,究竟是不是,看了下文你就明白了

1.数组(一维)

1.1数组的内存布局

int a[5];

定义了一个整形的数组a,其内存布局示意图如下:


在这里插入图片描述

首先编译器会根据定义数组的类型与元素个数划分出一块连续内存,再与a关联起来

a作为右值时 1,代表数组首元素的首地址而非数组的首地址,a不能作为左值

a[0]、a[1]…这些是a的元素,我们可以通过其访问这些元素的内容,但并不是说他们是这些元素的名字(了解即可,不求甚解)

1.2 a、&a、&a[0]的区别

这三者的区别,可能有朋友会傻傻的分不清,前面我们说了,a作为右值时代表数组首元素的首地址而非数组的首地址(不理解没关系,下面用实例证明),借用陈老师的《C语言深度解剖》里的话“省政府和市政的区别----&a[0]和&a 的区别”,&a表示整个数组的首地址如同四川的省政府在成都,&a[0]表示数组首元素的首地址如同成都的市政府也在成都,二者虽然在数值上是相等的,但物理意义完全不相同,而a与&a[0]则是等价的

实例证明:

#include "string.h"
#include "stdio.h"
int main()
{
    
    int a[5]={1,2,3,4,5};
    printf("%d %d %d\n",&a[0],a,&a);
    printf("%d %d %d",&a[0]+1,a+1,&a+1);
    return 0;

}

运行截图:
在这里插入图片描述
&a[0],a,&a的值都为6422016(编译器分配的内存地址)看似没什么区别

但是:

&a[0]+1,a+1,&a+1的值分别为6422020、6422020、6422036,前两者的由6422016+sizeof(int)(首元素的首地址加上元素类型所占的空间)得来,可见二者没有任何区别,而&a+1则是由6422016+sizeof(int)*5(数组的首地址加上数组占的大小)得来

其实是通过物理意义来理解,“+1”就是在原有基础上移动一个单位,前二者表示元素的首地址其基础就是元素,后者表示的是数组的首地址其基础就是数组

相信看到这的朋友对数组已经有了相当的了解,接下来就来看看指针——“披着羊皮的狼”

2.指针

2.1指针的内存布局

int *p

定义指针p,其内存布局示意图如下:
在这里插入图片描述
如上图所示,我们把 p 称为指针变量,p 里存储的内存地址处的内存称为 p 所指向的内存,指针变量 p 里存储的任何数据都将被当作地址来处理,变量p本身的地址未知(根据编译器分配的不同而不确定)

*p前面的类型说明p所指向的内存存的数据类型

2.2 “*”好比门钥匙

如何理解 “ * ”?

我们已经知道指针变量p里面存的是且只能是地址,如果把这个地址比作是门牌号(某小区几单元几楼几号),那么 “ * ” 就好比是门钥匙,要知道门里有什么,那必须的要打开门吧,同理要知道地址对应的内存存了什么东西就必须要这把“钥匙” “ * ”

2.3 int * p = NULL 和 * p = NULL 有什么区别?

这是指针中比较经典的一个问题

首先说NULL在数值上和0是相等的,但和0的意义完全不同

在stdio.h中NULL的定义如下:

#if !defined(NULL) && defined(__NEEDS_NULL)
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

C++中被宏定义为0,C中被宏定义为空指针常量

int * p = NULL这句代码的意思是:定义一个指针变量 p,其指向的内存里面保存的是 int 类型的数据;在定义变量 p 的同时把 p 的值设置为0x00000000,而不是把*p 的值设置为 0x00000000

*p = NULL这句代码的意思是:给指针变量p所指向的内存赋值为0

2.4 p、&p、*p、p+1、*p+1、 *(p+1)、p[1]、&p[1]

  • p为指针变量,存储所指向内存的地址,类似于数组首地址a
  • ‘&’都不陌生,很明显这里是取地址符号,&p表示求取指针变量p本身的地址
  • *p带上了“钥匙”对吧,那它的功能必定就是获取“房间”里的东西,没错 *p就是获取所指向地址内存所存储的数据类似于数组a[0]
  • p+1表示在p的基础上移动一个“单位”,这里的“单位”就相当于数组中的下一个元素
  • *p+1表示在 *p的基础上加上1,这里的1理解为阿拉伯数字1,e.g. 如果 *p=1,那么 *p+1=2,注意这里并不是理解为“1+1=2”,而是1的ascll码‘0x31’+1=‘0x32’,‘0x32’对应就为字符2
  • *(p+1)很好理解,就是C语言运算符的优先级而已,不用说()的优先级当然高于 *
  • p[1]等价于*(p+1),这玩意就是“披着羊皮的狼”不注意还真会弄成是数组,这玩意完全可以当成数组元素那样理解,但和数组没有半毛钱关系,e.g. char *p=“Exclusive is handsome.”,p[0]=‘E’,p[1]=‘x’…
  • &p[1]就是取当前存储内容对应的地址

下面上代码帮助大家理解:

#include "string.h"
#include "stdio.h"
int main()
{

    int *p="ExclusiveTP is handsome.";
    printf("%d\n",sizeof(p));
    printf("%d %d %d %d\n",p,&p,p+1,&p[1]);
    printf("%c %c %c %c",*p,*(p+1),*p+1,p[1]);
    return 0;

}

运行结果截图:
在这里插入图片描述
到这里我们现在可以明确分辨出前言部分中Usart_Printf(u8 *str)定义的是一个指针变量str,至于str[data]不过是“披着羊皮的狼”而已

3.指针数组与数组指针–傻傻的分不清

啥情况,指针数组?数组指针?不是说指针和数组没有半毛钱关系吗?^ - ^

是的,指针和数组没有半毛钱关系,但在某种情况下二者是可以结合的,就比如‘+’,‘-’都是算术运算符号,二者是独立存在的没有关系,但你总是会遇见加减混合运算吧,指针数组和数组指针就可以理解为混合体

3.1指针数组与数组指针的内存布局

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决
定,它是“储存指针的数组” 的简称

数组指针:首先它是一个指针,它指向一个数组,在 64 位系统下永远是占 8 个字节,
至于它指向的数组占多少字节,不知道,它是“指向数组的指针” 的简称

二者定义如下:
int *p1[5];
int (*p2)[5];

哪一个是指针数组?哪一个是数组指针?

这里需要明白一个符号之间的优先级问题,“ [] ” 的优先级比 “ * ” 要高,p1 先与 “ [] ” 结合,构成一个数组的定义,数组名为 p1, int *修饰的是数组的内容,即数组的每个元素,那现在我们清楚,这是一个数组,其包含 5 个指向 int 类型数据的指针,即指针数组

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

下面附上二者的内存布局图帮助理解

在这里插入图片描述
老道只能“抛砖引玉”,因为我实则是搞硬件的,对数组和指针的要求并不是很高^ - ^

加油!拼命干才会百分百成功,下一篇再见!
  1. 右值为放在等号右边的值,左值即为等号左边的值 ↩︎

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值