Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1323778
  • 博文数量: 168
  • 博客积分: 2124
  • 博客等级: 大尉
  • 技术积分: 2590
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-16 23:51
文章分类

全部博文(168)

文章存档

2014年(6)

2013年(74)

2012年(71)

2011年(17)

分类: C/C++

2011-11-10 16:16:00

因为看的是电子书,所以在网上把笔记做了~
 
 
 
内存分为三个部分:静态区,栈(stack),堆(heap)。
 
静态区:保存自动全局变量和static 变量(包括static 全局和局部变量)。静态区的内容
在总个程序的生命周期内都存在,由编译器在编译的时候分配。
 
栈:即我们通常说的堆栈保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。(也就是说我们在main函数里定义的函数都是在这里,传递参数等等)

堆:由malloc 系列函数或new ( C++的哦,亲 )操作符分配的内存。其生命周期由free 或delete 决定。 (也就是说,malloc分配的内存都是从堆上的内存块去分的)
在没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但容易出错。
 
 
 
 
常见的内存错误及对策
 
一。结构体
 

1.  指针没有指向一块合法的内存 常见的就是定义1个指针,但是没有为其指向一个合法的内存~~不用时“拴在”NULL

2.  结构体成员指针未初始化

    struct student
    {
     char *name;
     int score;
    }stu,*pstu;
    Int main()
    {
     strcpy(stu.name,"Jimy");
     stu.score = 99;
     return 0;
    }

 

假如这么改

 

  1. Int main()
  2. {
  3. pstu = (struct student*)malloc(sizeof(struct student));
  4. //注意,不要误写成sizeof(strut stuent *)了,这样的话pstu分配的内存将不够!!!
  5. //想想值是什么?毕竟他是指针,4~
  6. strcpy(pstu->name,"Jimy");
  7. pstu->score = 99;
  8. free(pstu);
  9. return 0;
  10. }

 

同样,我们并没有为name分配内存,

分配方法为 

  1. pstu->name = (char *)malloc((strlen("Jimy")+1)*sizeof(char));

 

(sizeof,strlen初学的时候确实易混,马上写个博文)

 
 
二。函数的入口校验
 
 
一般在函数入口处使用assert(NULL != p)对参数进行校验。在非参数的地方使用
if(NULL != p)来校验。
 
 
但这都有一个要求,即p 在定义的同时被初始化为NULL 了
 
assert 是一个宏,而不是函数,包含在assert.h 头文件中。如果其后面括号里的值为假,则程序终止运行,并提示出错;如果后面括号里的值为真,则继续运行后面的代码。
 
 
 
 

三.内存越界问题

 

为指针分配内存,但是分配的不足出现越界行为

 

 

  1. char *p1 = “abcdefg”;
  2. char *p2 = (char *)malloc(sizeof(char)*strlen(p1));
  3. strcpy(p2,p1);

 

 

p1 是字符串常量,其长度为7 个字符,但其所占内存大小为8 byte。初学者往往忘了字符串常量的结束标志“\0”。这样的话将导致p1 字符串中最后一个空字符“\0”没有被拷贝到p2 中。

 

解决的办法是加上这个字符串结束标志符:

 

 

  1. char *p2 = (char *)malloc(sizeof(char)*strlen(p1)+1*sizeof(char));
  2. /*或写为*/char *p2 = (char *)malloc(sizeof(char)*(strlen(p1)+1));

 

 

注意这种形式

 

  1. char a[7] = {‘a’,’b’,’c’,’d’,’e’,’f’,’g’};

 

另外,不要因为char 类型大小为1 byte 就省略sizofchar)这种写法。这样只会使你的代码可移植性下降。

 

 

越界另外常见的例子就是数组的边界,详见C 陷阱的 边界不对称的章节

 

记住,定义完以后如果不确定其值,一定要对其进行初始化

 

  1. int i = 0;
  2. char *p = NULL;//尤其是指针
  3. int a[10] = {0};

 

 
 

四.内存泄露问题

 

 

会产生泄露的内存就是堆上的内存,内存泄露说白了就是用mallocnew后没有及时freedelete(注意之前说的,释放完毕后,立即对指针拴住,指向NULL

 

 

  1. char *p = (char *)malloc(100);

 

 

只能通过指针变量p 来操作这块内存。这块内存本身并没有名字,对它的访问是匿名访问。

 

如果所申请的内存块大于目前堆上剩余内存块(整块),则内存分配会失败,函数返回NULL

 

注意这里说的“堆上剩余内存块”不是所有剩余内存块之和,因为malloc 函数申请的是连续的一块内存。

 

既然malloc 函数申请内存有不成功的可能,那我们在使用指向这块内存的指针时,必

须用ifNULL = p)语句来验证内存确实分配成功了

 

 

我们不能两次malloc,一次free,这样会造成内存泄露,同样,我们对一个malloc不能两次free,解释为下:

 

If a memory is freed twice, it will cause some unknown problems in somewhere later if it didn't crash at the second free。

 

 

关于用malloc分配0字节的内存:

 

申请0 字节内存,函数并不返回NULL,而是返回一个正常的内存地址。但是你却无法使用这块大小为0 的内存。这这时候ifNULL = p)语句校验将不起作用。

 

 

~~函数返回栈内存。这是初学者最容易犯的错误。比如在函数内部定义了一个

数组,却用return 语句返回指向该数组的指针。解决的办法就是弄明白栈上变量的生命周期。

 

 

32位的WIN平台下,不管申请 了多少空间都不会造成内存耗尽的现象,因为WIN使用的是虚拟内存,如果内存被申请完了就自动用硬盘代替了。所以如果申请了过分大的空间 ,可能你的硬盘就受着很大的压力了。

 

 

UNIX环境高级编程》中第七章的一段话:

  
大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针等等。这意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的,但是因为这种错误不会很快就暴露出来,所以也就很难发现。将指向分配块的指针向后移动也可能会改写本块的管理信息。

 

总之,内存申请和释放是个很复杂的问题,没有想想中的那么顺利~~

 

参考资料:

 

1.http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=824704

 

2.《c语言深度剖析》 林正冲

 

 
 
阅读(1719) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~