《C专家编程》读书笔记7
2011-08-04 10:35
211 查看
第七章 对内存的思考
calloc函数与malloc类似,但它在返回指针之前先把分配好的内存内容都清空为零。Realloc函数改变一个指针所指向的内存块地大小,既可以将其扩大,也可以把它缩小,它经常把内存拷贝到别的地方然后将指向新地址的指针返回。
堆内存的回收不必与它所分配的顺序一直(它甚至可以不回收),所以无序的malloc/free最终会产生堆碎片。
释放或改写仍在使用的内存(称为“内存损坏”)。
未释放不再使用的内存(称为“内存泄露”)。
在可能的时候使用alloca()来分配动态内存。当离开调用alloca的函数时,它所分配的内存会被自动释放。有些人不提倡使用alloca,因为它并不是一种可移植的方法。
段错误或段违规是由于内存管理单元(负责支持虚拟内存的硬件)的异常所致的,而该异常则通常是由于解除引用一个未初始化或非法值的指针引起的。
和总线错误不同,段错误更像是一个间接的症状而不是引起错误的原因。
如果未初始化的指针恰好具有未对齐的值(对于指针所要访问的数据而言),它将会产生总线错误,而不是段错误。
通常导致段错误的几个直接原因:
解除引用给一个包含非法值的指针。
解除引用一个空指针(常常由于从系统程序中返回空指针,并未经检查就使用)。
在未得到正确的权限时进行访问。例如,试图往一个只读的文本段存储值就会引起段错误。
用完了堆栈或对堆空间(虚拟内存虽然巨大但绝非无限)。
以发生频率为序,最终可能导致段错误的常见编程错误是:
1. 坏指针值错误:在指针赋值之前就用它来引用内存,或者向库函数传送一个坏指针。第三种可能导致坏指针的原因是对指针进行释放之后再访问它的内容。可以修改free语句,在指针释放之后再将它置为空值。
free(p); p=null;
2. 改写(overwrite)错误:越过数组边界写入指针,在动态分配的内存两端之外写入数据,或改写一些堆管理数据结构(在动态分配的内存之前的区域写入数据就很容易发生这种事情)。
p=malloc(256); p[-1]=0; p[256]=0;
3. 指针释放引起的错误:释放同一个内存块两次,或释放一块未曾使用malloc分配的内存,或释放仍在使用中的内存,或释放一个无效的指针。
for(p=start;p;p=p->next)在这样的循环中迭代一个链表,并在循环体内使用free(p)。这样,程序就会对已经释放的指针进行解除引用操作,从而导致不可预料的错误。
struct node *p, *start, *tmp;
for( p=start;p;p=tmp)
{
tmp=p->next;
free(p);
}
7.1 Intel 80x86系列
7.2 Intel 80x86内存模型以及它的工作原理
7.3 虚拟内存
7.4 Cache存储器
7.5 数据段和堆
就像堆栈段能根据需要自动增长一样,数据段也包含了一个对象,用于完成这项工作,这就是堆。堆区域用于动态分配的存储,也就是通过malloc函数获得的内存,并通过指针访问。堆中的所有东西都是匿名的——不能按名字直接访问,只能通过指针间接访问。从堆中获取内存的唯一办法就是调用malloc(以及同类的calloc、realloc等)。calloc函数与malloc类似,但它在返回指针之前先把分配好的内存内容都清空为零。Realloc函数改变一个指针所指向的内存块地大小,既可以将其扩大,也可以把它缩小,它经常把内存拷贝到别的地方然后将指向新地址的指针返回。
堆内存的回收不必与它所分配的顺序一直(它甚至可以不回收),所以无序的malloc/free最终会产生堆碎片。
7.6 内存泄露
堆经常会出现两种类型的问题:释放或改写仍在使用的内存(称为“内存损坏”)。
未释放不再使用的内存(称为“内存泄露”)。
在可能的时候使用alloca()来分配动态内存。当离开调用alloca的函数时,它所分配的内存会被自动释放。有些人不提倡使用alloca,因为它并不是一种可移植的方法。
7.7 总线错误
总线错误几乎都是由于未对齐的读或写引起的。它之所以称为总线错误,是因为出现未对齐的内存访问请求时,被阻塞的组件就是地址总线。段错误或段违规是由于内存管理单元(负责支持虚拟内存的硬件)的异常所致的,而该异常则通常是由于解除引用一个未初始化或非法值的指针引起的。
和总线错误不同,段错误更像是一个间接的症状而不是引起错误的原因。
如果未初始化的指针恰好具有未对齐的值(对于指针所要访问的数据而言),它将会产生总线错误,而不是段错误。
通常导致段错误的几个直接原因:
解除引用给一个包含非法值的指针。
解除引用一个空指针(常常由于从系统程序中返回空指针,并未经检查就使用)。
在未得到正确的权限时进行访问。例如,试图往一个只读的文本段存储值就会引起段错误。
用完了堆栈或对堆空间(虚拟内存虽然巨大但绝非无限)。
以发生频率为序,最终可能导致段错误的常见编程错误是:
1. 坏指针值错误:在指针赋值之前就用它来引用内存,或者向库函数传送一个坏指针。第三种可能导致坏指针的原因是对指针进行释放之后再访问它的内容。可以修改free语句,在指针释放之后再将它置为空值。
free(p); p=null;
2. 改写(overwrite)错误:越过数组边界写入指针,在动态分配的内存两端之外写入数据,或改写一些堆管理数据结构(在动态分配的内存之前的区域写入数据就很容易发生这种事情)。
p=malloc(256); p[-1]=0; p[256]=0;
3. 指针释放引起的错误:释放同一个内存块两次,或释放一块未曾使用malloc分配的内存,或释放仍在使用中的内存,或释放一个无效的指针。
for(p=start;p;p=p->next)在这样的循环中迭代一个链表,并在循环体内使用free(p)。这样,程序就会对已经释放的指针进行解除引用操作,从而导致不可预料的错误。
struct node *p, *start, *tmp;
for( p=start;p;p=tmp)
{
tmp=p->next;
free(p);
}