如何解决C/C++指针使用的问题?

指针是一把双刃剑,用得好所向披靡,如果用的不好,则会让你的程序出现各种各样的问题,最终导致程序崩溃。

常常有人这么说,C/C++程序员大部分的BUG都是在处理由指针引起的BUG上,比如常见的内存问题、野指针等等。那我们应该如果避免出现指针使用上的问题呢?

不正确的指针声明方式

来看看如下的声明:

int *pH1,pH2;

上面的声明本身没有错误,不过,可能跟我们想的不同,它把pH1声明为整数指针,而把pH2声明为整数。

我们都知道,指针声明和声明其他变量一样,唯一不同的一点就是要在变量前加 *。而上面的星号紧挨着数据类型,而pH1前面加个空格。这样的位置对编译器来说没有区别,但是很容易导致我们看上去pH1和pH2都是整数的指针,但是只要pH1才是整数的指针。

正确的写法如下:

int *pH1,*pH2;

这样,两个变量均声明为整数指针。但是有注意到了吗,没有初始化,这样会导致野指针?

使用指针前必须要初始化

如果在初始化指针之前就使用了指针会导致运行时错误,通常这种指针我们称我为野指针。野指针也叫悬挂指针,是指向“垃圾”内存的指针,使用“野指针”会让程序出现不确定的行为。导致运行时错误。

  • 野指针产生的原因

1、 指针变量没有初始化。因此,创建指针变量时,该变量要被置为NULL或者指向合法的内存单元。
2、指针p被free之后,没有置为NULL,让人误以为p是个合法的指针。
3、指针跨越合法范围操作。不要返回指向栈内存的指针或引用

野指针:随机指向一块内存的指针(容易造成内存泄漏) 不一定每次都产生段错误,也许有一次刚好分配到一个已经分配好的空间 。

  • 如何避免野指针?

来看看个例子:

#include <stdio.h>

int main()
{
  int *ptr;

  printf("ptr:%d\n",&ptr);

  return 0;
}

编译运行:

在这里插入图片描述

这里没有初始化ptr变量,因此它会导致包含垃圾数据,如果ptr中的内存地址超出了应用程序的合法地址空间,这段代码很可能就会执行过程中终止,否则打印出来的就是正好位于那个地址的数据,而且会表示整数。

指针本身并不能告诉我们它是否有效,所以,不能只能靠检查指针的内容来判断它是否有效,不过,有如下方法可以用来处理未初始化的指针。

使用NULL来初始化指针
用assert函数
使用第三方工具

把指针初始化为NULL更容易检查是否使用正确,例如:

int *ptr= malloc(10 * sizeof(int));
if(ptr== NULL) {
    // malloc分配内存失败
} else {
    // 处理ptr
}

我们还可以使用assert函数来测试指针是否为空值,下列测试了ptr变量是否为空值。如果表达式为真,那么什么都不会发生,如果表达式为假,那么程序会终止。例如:

assert(ptr != NULL);

这样,指针为空的话程序就会终止。函数assert在头文件assert.h上声明。

错误使用解引用

星号在C/C++中还有一个名字就是“解引用”。它的意思就是解释引用,说的通俗一点就是,直接去寻找指针所指的地址里面的内容,此内容可以是任何数据类型,当然也可以是指针。

声明和初始化指针的常用方式如下:

int inum = 10;
int *ptr = &inum;

错误的声明方式:

int inum = 10;
int *ptr;
*ptr = &inum;

注意到最后一行的解引用操作。我们试图把num的地址赋给ptr所指向的内存地址。指针ptr还没有初始化,这里我们误用了解引操作,正确的写法如下:

int inum = 10;
int *ptr;
ptr = &inum;、、// '&'的作用就是把inum变量在内存中的地址给提取出来

&表示的是引用,有&符号,就表示函数内的变量和主函数的变量是同一个,函数内改变它的值,主函数相应的变量也就跟着改变了;没有&符号,就表示函数内的变量是主函数的变量的一个副本,在函数内改变其值,是不会改变主函数中变量的值的。

注意: & 在C/C++中具体 3种语义, 按位与,取地址 和 声明一个引用。

错误使用sizeof操作符

错误使用sizeof操作符的一个例子试图检查错误边界但方法错误,来看看个例子:

#include <stdio.h>

int main()
{
	 int szbuf[20]= {0};
	 int *pbuf = szbuf;
	 printf("sizeof = %d\n",sizeof(szbuf));
	 for (int i = 0; i < sizeof(szbuf); i++)
	 {
	    printf("pbuf:%d\n",*pbuf);
	    *(pbuf++) = 0;  	
	 }
	 
	return 0;
}

上面的sizeof(szbuf)表达式返回了80,因此缓冲区长度以字节计是80(20*4)字节元素。上面的for执行了80次而不是20次。最终导致内存访问异常,产生段错误。

可以这样该为:

#include <stdio.h>

int main()
{
  int szbuf[20]= {0};
  int *pbuf = szbuf;
  printf("sizeof = %d\n",sizeof(szbuf)/sizeof(int));
  for (int i = 0; i < sizeof(szbuf)/sizeof(int); i++)
  {
     printf("pbuf:%d\n",*pbuf);
     *(pbuf++) = 0;  
	
  }
return 0;
}

编译运行:

在这里插入图片描述

这个改进就是在for表达式条件中用sizeof(szbuf)/sizeof(int)来编码上面的内存访问异常情况发生。

总结

指针就是一个重要却又充满了麻烦的特性。使用指针的一个理由是在作用域以外使用引用语义。
在C++ 中有智能指针提供了一种模仿指针同时支持边界检查等方法,而C没有智能指针,所有使用指针时候我们需要:

1、当没有指针指向时,要置为NULL
2、给指针指向的空间赋值时,一定要给指针分配空间
3、检查是否分配成功
4、分配成功之后,初始化
5、使用时,注意不要越界
6、使用完之后,free
7、free之后再次置NULL

参考: 深入理解C指针

在这里插入图片描述

欢迎关注微信公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值