使用C指针的几个基本注意点 [李园7舍_404]

原创 2013年12月05日 14:47:49
相关笔记:C指针和堆空间C malloc()实际分配空间大小


C中,使用动态分配的形式使用堆内存空间。涉及“动态内存分配函数(malloc)”,“动态内存释放函数(free)”。初学包含指针的程序设计语言,对堆空间的使用会出现很多的问题似乎是不可避免的过程。总结一下关于动态分配的笔记,关于内存管理此篇笔记处于初级水平,还未到深入地步。


C程序中实现动态分配离不开指针。动态内存分配涉及“指针的定义”,“使用malloc函数”,“使用分配成功的堆空间”,“释放堆空间”。每一步都可能会程序带来潜在的错误。


1 未初始化的指针

对于C语言程序来说,系统会为初始化的全局变量赋予初值0。未初始化变量的值不定。在MinGW(gcc for windows)验证如下:

#include <stdio.h>
#include <stdlib.h>

int *pG1, *pG2;

int main()
{
    char *pL1, *pL2;
    printf("pG1: %p, pG2: %p; pL1: %p, pL2:%p\n", pG1, pG2, pL1, pL2);
    return 0;
}


编译后的警告石两个局部变量指针未初始化,执行结果如下:

未初始化指针值执行结果

  • 未初始化全局变量指针的值为0。未初始化局部指针变量的值不定。
  • 指针值所代表的内存中内容不定。(如果在以上程序中输出pL1pL2中的内容,则会导致程序出现异常)。


未初始化的指针变量就成为了野指针(指针的随机值代表的内存不可用)。


2 malloc()内存

可先参见位经malloc()函数申请分配的堆内存在计算机中的形式:计算机中的堆


经malloc()分配过得堆内存结构如下:

Read FromThe C Programming Language》。


可用的堆内存块以“可用堆内存链表”的形式存在。malloc()进行动态分配的特点:

  • malloc()根据用户所需分配内存的大小n (bytes)在“堆链表”(见未使用过得堆内存)里搜索。直到搜索到一个大于等于n字节的堆内存块为止。如果此堆内存块的大小刚好为n,则直接将首地址返回给用户;如果此内存块的大小大于n,则将此块堆内存分裂,将大于n部分的堆内存留在可用堆内存中,以“堆链表”的形式和其它未分配的堆内存发生联系。
  • 如果整个堆链表所代表的堆内存块都没有大于等于n的堆内存块,系统将给“堆链表”链接一个更大的区域供其使用。要是这一步也失败了,malloc()函数就返回NULL给用户。


malloc()函数分配内存成功则返回可用堆内存块的首地址,若分配失败则返回空。在使用malloc()后一定要判断堆内存是否成功。若对内存分配未成功使用指针操作内存也会使程序出现异常。动态分配内存时要采取以下结构:

char *pL =NULL;
……
pL       = (char *)malloc( sizeof(char) * size);
if(pL)
{
      …
}

分配成功后,得到的堆内存首地址一定要保存,不然后来无法释放堆内存而造成内存泄露。而且不可使用未初始化的pL指向的内存块。



3 free()内存

当使用free()函数释放堆内存的时候,free()函数将堆内存插入到于要释放堆内存地址最邻近的一个位置上,尽可能的使堆内存以大块的形式存在而不至于让堆内存称为碎片。


释放未指向任何堆内存块的指针也会造成内存泄露。所以在释放指针前的一个基本操作是判断指针内容是否为空,free(p)后只是将p指向的内存回收,p的值依旧存在,为避免再次使用p的值还需要将p赋值为NULL(因为使用指针前都会判断是否为NULL)。释放堆内存块采取这样的程序结构:

if(pL)
{
      free(pL);
      pL      = NULL;
}


4指针赋值为NULL的道理

有笔记“C中的void和NULL”表面引用NULL指针的后果。为了更好的利用指针,避免野指针(指针所指的内存块不可用)的使用在所有使用指向堆内存块的指针前都采取如此的结构:

if(p)
{
      //通过指针操作堆内存
……
}

定义指针后将其值赋值为NULL。此时指针指向的内存地址为NULLNULL对指针的赋值是将指针置成空指针(什么也没有指向)还是将指针指向了一段特殊的地址取决于编译器,编程中我们不需要了解NULL到底代表什么,只需要用NULL来避免指针带来的后果。

定义指针后将其赋值为NULL之后的好处在于避免系统给予局部指针变量的随机值,我们在使用指针前(malloc()除外)都判断一下指针的值是否为NULL,只有在不为空的情况下才能对此进行操作,如free(p),若在不判断p是否为空的情况下进行free(p)操作则会造成内存泄露。


完全使用完某个指针或释放指向堆内存的指针后,将其值赋值为空。指向堆内存的指针在释放完需要赋值为空的理由见free ()堆内存。对于指针定义时初始化和完全使用完指针后再将其值赋为NULL的道理在于所有使用指针的语句前都会有有判断指针是否为NULL的语句。尤其是在子函数内判断指向堆内存块的指针实参是否为NULL



5在含指针参数的函数内使用断言

(1)用断言判断指针是否为NULL

判断指针是否为NULL的主要针对对象是指向堆内存的指针。比如在以下内存拷贝函数中:

flag  my_strcpy(char  *StrTo,  char  *StrFrom)
{
      if(!StrTo || !StrFrom)
      {
            return  -1;
      }
      char  *StrToL, *StrFromL;
      StrToL      = StrTo;
      StrFromL    = StrFrom;
      while(*StrToL++ = * StrFromL++)
            NULL;
      return 0;
}

程序中首先判断两个地址是否为空。判断StrTo是为了了解StrTo是否指向一段空间。当然若StrTo指向一个常数,往后拷贝操作还得出错。


像这样带指针参数的子函数内都很有必要有这么一段判断指针是否为NULL的语句,故而可以将这样的代码写成函数来供大家使用,再考虑此代码段比较小可以用宏代替。这样的(带参数)宏可称为断言,因为当指针未空时就退出子函数(如assert())。


如以上一段判断子函数是否为空可以用如下宏代替,形成一个断言:

#define  MY_ASSERT(pStrTo, pStrFrom)     if(!StrTo || !StrFrom)   	\
					 {                   		\
                                              return  -1;    		\
					 }

然后在每个程序中直接调用MY_ASSERT(pStrTo, pStrFrom);即可。由于这样的宏(断言)可能供许多函数的使用,所以一定要保证它的正确性。


(2)内存块重叠

内存块重叠指多个指针指向的内存有重叠的情况。对内存块的操作是否会影响源内存块的内容(如内存数据拷贝)。

两指针指向的内存块重叠

如上图将p2指向内存的数据拷贝给p1代表的内存中去后,p2指向的内存块数据也被改变。堆内存块的操作不要有副作用。


6总结

(1)NULL(因其特殊性)来统一标识指针的可用性。使用指针前都应该判断一下指针是否为NULL

(2)将局部指针变量初始化为NULL(消除系统给其赋的随机值,系统为其赋随机值也就造就了野指针)。

(3)指针用于指向堆内存时需要注意:

  • malloc()后一定要判断是否malloc()成功。malloc()成功后一定要保存所分配堆内存块的首地址。

  • 使用堆内存块前要初始化。

  • 使用堆内存块不可越界。

  • 正确释放每个堆内存块。且释放后将指针的值重新赋值为NULL

(4)使用指针前都应该判断一下指针是否为NULL


Note Over。

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

C指针和堆空间 [李园7舍_404]

相关笔记:C指针和堆空间、C malloc()实际分配空间大小。 0 堆内存的在计算机内存中的形式 根据《The C Programming language》推测得到堆内存,图中的Hea...

2014深圳锐明视讯校园招聘 [李园7舍_404]

好几天前的笔试。今天去九龙坡面试了一趟。遇上辅导员找咱还三方协议,虽然是悄悄的拿的,但就是不想还回去。深圳锐明视讯的宣讲会流程为:宣讲会(1小时左右)-->笔试(1小时半),然后过两三天的时间部分同学...

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

51 下载程序到单片机内 [李园7舍_404]

大三,matlab+嵌入式+程序员培养。从此需要坚持对单片机的学习。我会记录我在学习中的点点滴滴。 此文主要记录下载目标程序到单片机内的步骤。 1.安装下载软件 一般在买单片机的时候厂家会附加一张光盘...

51 数码管静态显示 [李园7舍_404]

对于单片机的学习我一般不按照资料的顺序学习。我是照这个步骤将新知识掌握: 1.学习过程:阅读C语言程序,查看控制变量被赋予的值-->查看跟控制变量对应的相关的电路图-->被控制电路所接收到的输入信号会...

51 数码管动态显示 [李园7舍_404]

首先根据位选与段选理念(数码管静态显示一文)编写程序去控制不同数码管显示不同的数字。根据程序控制需要,在调试程序的过程中明白需要了解我们所要的控制对象(数码管)的一些特性。由于我自己拥有的这个单片机非...

C语言数据在内存中的存储类别 [李园7舍_404]

探究C语言不同数据类型在内存中的存储很有意思,也是学习c语言时的难点。1的内容推进2的理解,含各变量的小特性哦。   1.程序存储区域 程序(可执行文件)进行存储时分为三个区域:代码段,数据段,...

大话Static关键字篇

Static 篇,这里是我总结的关于Static的一些知识点,望对大家有所帮助,当然可能有错误点希望大家体谅! 1.static关键字的用途 简言之,Static就是在没有创建对象的情况下调用(方法/...

51 Keil语句延迟时间的精确计算 [李园7舍_404]

一、   软件模拟运行速度与硬件对应 我们首先要找到单片机的晶振为多大(一般在配套的使用说明书中有标识或者直接在实验板上面看,实验板上面标识的不是很明显),如图所示左下角标识。        ...

28BYJ-48步进电机连续正反转 的控制 [李园7舍_404]

1 硬件 步进电机:28BYJ-48 步进电机驱动:ULN2803 单片机:STC89C52   2 步进电机原理、驱动原理 2.128BYJ-48步进电机实物图   图1  ...

CEF使用的几个注意点

转自http://www.cnblogs.com/gongxijun/p/4857977.html     CEF为chrome浏览器的切入其他浏览器中的轻量级框架。 开发的客户端的时候,这是作为界...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)