相关笔记:C指针和堆空间、C malloc()实际分配空间大小。
0 堆内存的在计算机内存中的形式
根据《The C Programming language》推测得到堆内存,图中的Heap区域即为堆内存块(Heap区域的数目不代表计算机堆内存的真实数目)。
[1] 堆内存不连续。只有标识为Heap的才是堆内存。
[2] 在malloc()/free()看来,每个Heap所代表的的堆由两部分组成:Header +可给用户使用的堆内存。在Header中包含了“指向下一邻近高地址堆内存块的指针”、“本堆块的大小”。每次由malloc()函数分配给用户的堆内存也必须包含Header结构(且所占内存就在返回给用户使用的堆内存之前),这样是为了让malloc()/free()更好的管理堆内存。
[3] malloc()/free()函数操作的堆内存是如图所示的一个链(Heap1 -> Heap2 ->Heap3 ->Heap4 ->Heap1),可通过此链表访问到任意一段堆内存。所以,经malloc()函数实际分配得到的堆内存要比用户实际需求的要大一个Header,只是返回给用户的堆内存大小刚好是用户所需。free()释放时,也要根据Header的内容将此段曾供给用户使用过得堆内存释放到最邻近的一个堆块中去。
这就是内存中的堆内存。堆内存由用户用代码分配及回收。堆和栈的区别不仅在于内存的存在形式,在使用时栈一般拥有内存名即栈内存可以由内存名(变量名)直接访问,也可以通过地址(指针)访问栈内存。但对于堆内存来说,堆不存在内存名,只有通过地址(指针)访问。
1.在函数内返回堆空间
#include <stdio.h>
char *get_memory()
{
char *p;
p = (char *)malloc(sizeof(char) * 10);
return p;
}
int main(void)
{
char *p = NULL;
p = get_memory();
strcpy(p, "hello");
printf("%s", p);
free(p);
return 0;
}
函数执行结果:hello。因为:
- 执行get_memory函数,p所指的malloc分配的堆空间的首地址经p返回。栈中的p被释放,但malloc所分配的内存还在堆中,未经free释放。
- p = get_memory();使p存了malloc在堆中分配的地址的首地址。
- 以p为索引,使用堆空间。
- 以p为索引,释放malloc分配的堆空间。
编译器不支持的是在函数内返回栈空间的地址。栈空间的生命期完之后就被系统自动回收。
未被释放的堆空间可经子函数返回给父函数中的指针,让父函数的指针变量来操作堆空间,在父函数中使用完毕后再释放。
2.用指针来使用堆空间
- 定义指针后,释放堆空间后都应将指针赋值为NULL。若指针之上有地址值,而以此地址值为起始地址的内存空间不再可用,则就形成了野指针,野指针有潜在的危险。
- 在上一点的基础之上,使用指针前判断其值是否为NULL。
- 以指针为索引(堆内存无名),若malloc分配内存成功,初始化堆内存(malloc时,大小要不为0)。malloc前的强制转换类型规定了申请的堆内存将要存的数据类型。
- free堆内存后,指针保存的地址值还在,只是那块内存已经被回收了,所以需要再次将指针的值设为NULL,避免使用野指针。free内存时,按照逻辑来,防止内存泄露。
指针名所代表的4 bytes内存上存了堆内存的首地址后,访问这块堆内存内容跟平时使用指针差不多。可以以指针的形式访问(甚用p++ || ++p,堆内存首地址可不要丢失,留着释放),也可以使用下标的形式访问。
3.总结
对指针赋施操作时,需要辨清是操作为指针变量本身分配的内存(32 bit下为4 bytes)还是操作指针指向的内存块。所谓,指针所指向的内存是指此块内存的首地址值是指针的值,也是系统为指针变量分配内存内的值。
对前者进行操作如对指针赋值,这种操作要保证所赋的值所代表的内存块可用。在内存块可用的情况下定要保证内存块经初始化后再使用,若内存块不可用则就造就了野指针。
对后者进行操作时,若指针原本就是一野指针(即指针所指的内存块不可用),则应将指针赋值为NULL以避免使用野指针,消除野指针的潜在危险。若指针所指内存块可用,一定要认清此内存块,不要有越界的操作。
另一个方面,指针若是指向堆内存块,则一定要让堆内存块得到准确的释放。
C Note Over。