C语言修行之基础篇 (三) 指针与野指针


指针

什么是指针?

指针的实质就是变量,它与普通变量没有区别。指针全称为指针变量,简称为指针。

// 定义普通变量和指针变量
// a的实质就是编译器里的符号,然后将a与这个空间绑定起来
int a;             // 定义了int型变量,名字为a
int *p;            // 定义了一个指针变量,名字为p;p指向一个int型变量

内存分布
在这里插入图片描述
既然指针变量和普通变量一样,那他们的赋值就可以是一样的吗?
a = 8; 可以操作
p = 8; 编译器不让指针这么操作。虽然指针变量实质上也是普通变量,但是它们的用途和普通变量不同。
区别:
普通变量a空间存放的int类型的数据
指针变量p空间存放的是另外一个变量的地址,而不是存放int类型数据
p = (int *)8; 可以操作,在这里将8这个数字强制类型转换为(int *)类型的8;这里相当于告诉编译器,这个8是个地址(而且是存放int类型变量的地址)。这就和p的类型相匹配。


为什么需要指针?

指针是为了实现间接访问。其实就是CPU的寻址方式中的间接寻址。
间接访问是CPU设计时决定的。这个决定让使用在CPU上的语言都需要实现间接寻址。
(Jave等上层语言对指针进行封装,所以Jave看不见有使用指针,但是最终还是会调用间接访问;C语言越接近底层,对指针的封装不明显)

指针的使用3步:
定义指针变量、给指针变量赋值(绑定指针)、解引用
注:指针必须给它赋值才能解引用它,不然很容易出问题。

int a =34;
// 定义指针
int *p;
// 绑定指针,就是给指针变量赋值,指向另一个变量a(指针的用途就是指向别的变量)
p=&a;
// 将66放入p所指向的那个变量的空间中,这里就是a的空间
*p = 66;

当用int *p;定义一个指针变量,因为p是局部变量,所以也是遵循C语言局部变量的一般规律(定义局部变量未初始化,值是随机的),p未初始化则为随机的数。
这时去解引用p,相当于去访问地址为随机值的空间。但是这个访问的空间到底能不能被访问就不知道了。
指针绑定是为了让指针指向一个可以访问且应该访问的地方。
指针解引用是为了间接访问目标变量。
在这里插入图片描述


指针相关的符号

写出来的代码是给编译器看的。需要站在编译的角度写代码,编译器理解代码就是理解符号。正确使用和理解C语言中的符号就能写出你想要的效果。

星号“ * ”

“ * ”在C语言中有两种含义:乘号和指针符号,这两种相互没有关联。
“ * ”在指针中有两种含义:
第一种,指针定义时,*结合前面的类型用于表明要定义的指针类型
第二种,指针解引用,解引用时p指向的变量本身。

int *p;表明p的类型为int *,也就是p是指向int类型变量的指针
int *p1, *p2; // 定义p1和p2类型为(int *)
int *p1, p2; // 定义p1类型为int *,定义p2类型为int

取地址符 “ & ”

取地址符直接加在一个变量前面,然后取地址符与变量重新结合成一个符号,这个符号表示这个变量的地址。
为什么需要取地址符?
编译器在给每个变量分配出内存空间,并将a与这块的内存空间地址绑定。这个地址只有编译器知道,而程序员并不知道编译器随机给这段空间分配什么随机地址值。程序员要获取或操作这个地址时,就需要使用取地址符“ & ”

指针定义并初始化与指针定义后赋值的格式

指针定义并初始化的格式:int a=34; int *p =&a;
指针定义后赋值的格式:int a=34; int *p; p =&a;
这两种方式没有区别。第一种中的“ * ”是和前面的int类型结合起来表明p的类型,a的地址值是赋值给p本身。第二种中p=&a,p就是本身的赋值


左值与右值

在赋值符号“ = ”左边的就是左值;在符号右边的值就是右值。格式:左值 = 右值
当一个变量做左值时,编译器认为这个变量符号的真实含义是这个变量所对应的那个内存空间;
当一个变量做右值时,编译器认为这个变量符号的真实含义是这个变量的值,也就是这个变量所对应的内存空间中存储的那个数。

int a =6;
int b =8;

a = b;          // 左值为a,在这里更关心的是a的内存空间
                // 右值为b,在这里更关心的是b这个内存空间里所存储的值

野指针

野指针是什么?

野指针,就是指针指向的位置是不可知的(随机的,不确定的,没有明确指定的)
野指针很可能触发运行时段错误。因为指针变量在定义时没初始化,值为随机的。但我们去解引用这个随机的地址,得到的值自然是不可知的。

野指针引发的危害

野指针因为指向的地址是不可知的,所以会出现3种情况:
第一种,指向不可访问的地址。比如:访问操作系统OS的空间。结果会触发段错误。
第二种,指向可用的且没有什么特别意义的空间。比如:曾经使用过的栈(堆),后面被释放掉的空间。结果运行不会出错,也不会对当前程序造成损害。
第三种,指向一个可用的空间,而且这个空间中的变量在程序中使用。结果会导致程序出现离奇的错误,最后有可能导致系统崩溃。

怎么避免野指针?

野指针的错误原因是指针定义之后没有初始化,也就是没有赋值,然后去解引用它。
解决方法:在解引用指针之前,确保指针指向一个绝对可用的空间
1、定义指针时,同时初始化为NULL
2、在指针解引用之前,判断这个执政是否为NULL
3、指针使用之后,将它赋值为NULL
4、在指针使用之前,将其绑定一个可用地址空间

NULL是什么?

NULL在C/C++中定义为:
#ifdef __cplusplus                 // 定义这个符号表示当前是C++环境中
#define NULL 0                    // 在C++中NULL为0
#else
#define NULL (void *) 0        // 在C中的NULL是强制类型转换为void *的0
#endif

将0强制转换为void *是为了兼容指向任何类型的值
将指针指向NULL,相当于指向0地址处。

为什么要指向0地址?
第一,0地址处作为一个特殊地址(指针指向这里表示指针没有被初始化,表示野指针)
第二,这个地址0在一般的操作系统中都是不可访问的,如果程序员没有初始化就解引用它时,就会引起段错误。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bazinga bingo

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值