前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >C/CPP 指针变量 | 数组指针 | 指针数组 | 野指针 | 空指针

C/CPP 指针变量 | 数组指针 | 指针数组 | 野指针 | 空指针

作者头像
CtrlX
发布2023-03-21 11:17:33
发布2023-03-21 11:17:33
1.7K00
代码可运行
举报
文章被收录于专栏:C++核心编程C++核心编程
运行总次数:0
代码可运行

普通变量和指针变量

共性

PS:

可见这4个函数的汇编指令完全一致,无论是什么类型的指针变量,对指针变量的读写跟普通变量没有任何区别,所谓的指向只是描述指针变量的值时多少而已,就读写而言,指针变量跟普通变量没有任何区别。

特性

普通变量的值常常用于数学计算,而指针变量常常用来定位内存。

普通变量可以不赋初值,但是指针变量的初值必须万分慎重,因为未来的*操纵会以这个初值为目标内存地址,往里面读写数据(可以才C primer plus中看到相应分析)

所以给指针变量赋值一定要是合法合理的内存地址,读取非法的地址程序会修改其他的内存中的值导致程序崩溃,野指针。

空指针和野指针

野指针:定义了一个指针变量,如果没有进行初始化,系统就会有可能随机赋值一个地址给这个指针变量,也就是说,这个指向指向一个未知的区域。

空指针:空指针不是指向常数0,只指向地址0,即NULL,其实换句话说,指针的本质就是地址嘛,空指针就是指针本身的值(地址)为0空指针的作用是防止野指针的出现,因为我们不能知道野指针到底指向哪里,所以我们也无法判断一个指针是否是野指针,这样很危险,但如果养成将指针初始化为空指针的习惯,我们就能判断出这个指针是不是有效的(判断是不是NULL就可以了)通用指针一般都用在函数传参,实现所谓的“多态”,但到函数里面使用时,一般还是被转换成具体类型的指针。

指针变量的+-运算

指针变量的加减运算:也就是做地址偏移,不同 的指针类型偏移的步长不同。

PS:

区分指针数组int *a[3]和数组指针int (*a)[3],前者时存放指针的数组,后者是指向数组的指针。

指针数组和数组指针

引入

int *b可以用来定义数组(详见指针与数组定义字符串的区别文章)

代码语言:javascript
代码运行次数:0
运行
复制
int a = [1,2,3,4,5];
int *b = a;

char (*b)[5]用来指向数组

代码语言:javascript
代码运行次数:0
运行
复制
char a[] = "hellow";
char (*b)[5] = a;//指向的是char[]

char *b[5]用来存放指针的数组

代码语言:javascript
代码运行次数:0
运行
复制
char *b[] =["abc","def","ghi"];//指向常量区

数组指针(也称行指针)(关键)

定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

如要将二维数组赋给一指针,应这样赋值:

代码语言:javascript
代码运行次数:0
运行
复制
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a;    //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++;    //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,亦称行指针。

代码语言:javascript
代码运行次数:0
运行
复制
char (*)[4]p;//个人认为这样比较好理解,相当于二级指针,指向二维数组。char **p 

char (*)[4]p;相当于指向了一个有4列的数组,行指针,相当于列数已经固定了,看有多少行。

代码语言:javascript
代码运行次数:0
运行
复制
char a[3][4] = {"abc","def","ghi"};//3指3个元素,4是指每个元素占4个字节('\0')
/*三行四列
abc\0
def\0
ghi\0
*/
char (*p)[4] = a;

指针数组

定义 int *p[n]; []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值。 如要将二维数组赋给一指针数组:

代码语言:javascript
代码运行次数:0
运行
复制
int *p[3];
int a[3][4];
p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
for(i=0;i<3;i++)
	p[i]=a[i]

这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2] 所以要分别赋值。

PS:

内存映像象图

内容

权限

栈区

函数中的普通变量

可读可写

堆区

动态申请的内存

可读可写

静态变量区

static修饰的变量

可读可写

数据区

用于初始化变量的常量

只读

代码区

代码指令

只读

那么就有人问了?初始化arr的{“hello”, “world”, “shannxi”, “xian”};的是什么鬼? 这四个不是什么鬼,他们也存在在内存中,只是跟arr这个变量不在同一段空间,它们被分配在只读数据区,数组arr[4]的四个指针元素,分别存放着这四个字符串的首地址,想象一下,从栈区有四只无形的手指向数据区的空间。arr+1会跳过四个字节,。也就是一个指针的大小

区别

这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。 还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。 比如要表示数组中i行j列一个元素: *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

优先级:()>[]>*(优先级问题详见C语言优先级文章)

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

实例

代码语言:javascript
代码运行次数:0
运行
复制
int a[4][4],*p,*q[4];
//已知0<= i < 4,下面错误的赋值是?
//(A) p = a (B) q[i] = a[i] (C) p = q[i] (D) p = &a[2][1]

解析:(A)

p = a编译器给出error:不能将int(*)[4]类型的值分配到int *类型的实体。p是一个指针a是一个二维数组,需要用数组指针接收。

详细分析:

int *p:

p的类型是int*一级指针,大小4个字节,p存放的是一个整形地址。

int a[][]

a是个二维数组,相当于是一个二级指针,a存放的是一个二维数组的首地址。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 普通变量和指针变量
    • 共性
    • 特性
  • 空指针和野指针
  • 指针变量的+-运算
  • 指针数组和数组指针
    • 引入
    • 数组指针(也称行指针)(关键)
    • 指针数组
    • 区别
    • 实例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档