<<C语言深度剖析>>学习笔记之六之内存管理

来源:互联网 发布:淘宝零食店策划书 编辑:程序博客网 时间:2024/06/06 00:15

    C语言和内存打交道比较多的莫过于指针了.

 

    1.野指针:

        为防止野指针的出现,需要养成良好的编程习惯--指针要时刻要有明确的指向对象,否则其随机指向一块内存区域,哪个地址再引用就会引发莫名的BUG.为避免野指针,规范的编程习惯如下:

        定义指针时初始化为NULL;指针用完后还是赋值为NULL.

   

    2.内存的三个部分

        内存可以分为三个部分:静态区、栈、堆.

 

        2-1.静态区:保存自动全局变量和static变量(包括static全局和局部变量).静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配.

 

        2-2.栈:保存局部变量.栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁.其特点是效率高,但空间大小有限.

 

        2-3.堆:由malloc系列函数或new操作符分配的内存.其生命周期由free或delete决定.在没有释放之前一直存在,直到程序结束.其特点是使用灵活,空间比较大,但是容易出错.


   3.常见的内存错误及对策.

        3-1.指针没有指向一块合法的内存.

        主要是指指针没有初始化.如下面这个示例:       

#include <stdlib.h>#include <stdio.h>#include <string.h>typedef struct __student{    char *name;    int age;}student,*pstudent;int main(int argc,char **argv){    student stu;    strcpy(stu.name,"Se7en");    stu.age = 24;    printf("stu.name = %s\n",stu.name);    printf("stu.age = %d\n",stu.age);    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./initpoint Segmentation faultroot@seven-laptop:~/learn/C_Program# vim initpoint.c

可见报段错误了.原因分析如下:
    1).我们在定义stu这个结构体变量时,只为这个结构体内部name成员本身分配了4个字节,它并不一定指向一块合法的地址,它内部存放的只是一些乱码;

    2).调用strcpy往这个乱码地址存放数据是危险的,因为很有可能这块地址是它无权访问的.


因此,当我们操作一个指针时.一定要明确这个指针指向哪!!!!

 

将上述代码达到我们预期的输出结果,可以通过下面两种方式实现.

方式一:

#include <stdlib.h>#include <stdio.h>#include <string.h>typedef struct __student{    char *name;    int age;}student,*pstudent;int main(int argc,char **argv){    student stu;//    strcpy(stu.name,"Se7en");    stu.name = "Se7en";    stu.age = 24;    printf("stu.name = %s\n",stu.name);    printf("stu.age = %d\n",stu.age);    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./initpoint stu.name = Se7enstu.age = 24root@seven-laptop:~/learn/C_Program# vim initpoint.c
分析:这时stu.name是指向"Se7en"这个字符串的.编译器为这个字符串分配的地址肯定是合法的.


方式二:

#include <stdlib.h>#include <stdio.h>#include <string.h>typedef struct __student{    char *name;    int age;}student,*pstudent;int main(int argc,char **argv){    student stu;    stu.name = (char *)malloc(sizeof(char) * 50);    strcpy(stu.name,"Se7en");    stu.age = 24;    printf("stu.name = %s\n",stu.name);    printf("stu.age = %d\n",stu.age);    free(stu.name);    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./initpoint stu.name = Se7enstu.age = 24root@seven-laptop:~/learn/C_Program# vim initpoint.c

       

上述两种修改方式都是保证了我们要访问的stu.name指针有明确的指向.


    3-2.assert()宏

        被调用的函数经常参数是指针类型的.很多代码中都可以看到其使用assert()宏来判断传入的参数的合法性.assert()宏的意义如下:

        assert()括号内的内容为假则终止程序并提示出错;为真则继续运行后面的代码.

        因此,我们常用如下形式来判定传入的指针参数的合法性.

        assert(NULL != p);


    3-3.小心strlen()函数

        我们常用strlen()来计算一个字符串的长度,strlen()计算出来的长度是不包括"\0"的.因此需要分配多一个字节.示例:

#include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc,char **argv){    char *pStr = "HelloWorld!";    char *q = NULL;    printf("strlen(pStr) = %d\n",strlen(pStr));    q = (char *)malloc(sizeof(char) * strlen(pStr));    strcpy(q,pStr);    printf("strlen(q) = %d\n",strlen(q));    printf("%s\n",pStr);    printf("%s\n",q);    free(q);    q = NULL;    return 0;}
上述代码在GCC 4.2.2可以正常输出:

root@seven-laptop:~/learn/C_Program# ./malloc strlen(pStr) = 11strlen(q) = 11HelloWorld!HelloWorld!root@seven-laptop:~/learn/C_Program# vim malloc.c 
但是在VC6.0下运行报错.要在VC6.0正常运行并兼容GCC.代码修改如下:

#include <stdio.h>#include <stdlib.h>#include <string.h>int main(int argc,char **argv){    char *pStr = "HelloWorld!";    char *q = NULL;    printf("strlen(pStr) = %d\n",strlen(pStr));    q = (char *)malloc(sizeof(char) * strlen(pStr) + 1 * sizeof(char));    strcpy(q,pStr);    printf("strlen(q) = %d\n",strlen(q));    printf("%s\n",pStr);    printf("%s\n",q);    free(q);    q = NULL;    return 0;}
输出结果:

root@seven-laptop:~/learn/C_Program# ./malloc strlen(pStr) = 11strlen(q) = 11HelloWorld!HelloWorld!root@seven-laptop:~/learn/C_Program# vim malloc.c 
可见,GCC对代码前后的输出结果是一样的.


总而言之,如果要存放一个字符串,记得多分配一个字节就是了.


    3-4.malloc()函数注意检查返回值.

        malloc()函数意为从堆上分配出一块内存以供程序使用,但是内存分配有可能是失败的.因此,一定要检查其返回值.如下:     

char *p = (char )malloc(1024);if(NULL  == p)    {        //内存申请失败处理.    }

    3-5.free()之后的故事

    free()函数就是把这块内存和p之间的所有关系断开.它要和malloc必须一一对应.这时候特别注意的是,指针变量p本身保存的地址并没有改变,我们需要重新把p的值变为NULL.如下:

free(p);p = NULL;