很多新学C语言的童鞋在用到动态内存分配的时候,对选择哪种分配函数及其有何区别搞不清楚,那么下文就认真的讲讲它们的种种。
(1)C语言的内存分配方式
-
从静态存储区域分配
这些在程序编译的时候就已经分配好,且在程序的整个运行期间都固定不变。
使用这种方式分配内存空间的数据主要包括:
代码段(DATA Section)的程序自身语句文本(包括嵌在代码里的字符串常量),已经初始化的全局变量和静态变量 。
.BSS段的未初始化的全局变量。
- 在栈stack上分配
这种方式由系统自动分配,函数退出时也由系统自动释放。
使用这种方式分配内存空间的数据主要包括:
本地变量和函数参数
- 从堆heap上分配,亦称动态内存分配
程序在运行的时候用malloc类或new动态申请的内存,由用户自己负责用free或delete释放。动态内存的生存期由用户决定,使用非常灵活,但问题也最多。
使用这种方式分配内存空间的数据主要包括:
自定义输入缓存或用于接收用户输入数据的变量(数组、结构体等)
(2)跟内存申请相关的函数
void* malloc(size_t size)
作用:从堆中分配size个字节的空间,成功则返回空间首地址,失败则返回空指针。它的声明位于stdlib.h头文件中。
我们在编程中必须注意分配失败的情况,以使程序更加健壮和完备。所以一般会对其进行包装:
void *
xmalloc (size_t size)
{
void *value = malloc (size);
if (value == 0)
fatal ("virtual memory exhausted");
return value;
}
同时,由于它并没有对分配的空间进行任何操作,因此基本上malloc之后,需要调用函数memset来初始化。
struct foo *ptr;
...
ptr = (struct foo *) malloc (sizeof (struct foo));
if (ptr == 0)
abort ();
memset (ptr, 0, sizeof (struct foo));
void * calloc (size_t count, size_t eltsize)
作用:从堆中分配size个字节的已经清零的空间,成功则返回空间首地址,失败则返回空指针。它的声明位于stdlib.h头文件中。
它的实现等同于:
void *
calloc (size_t count, size_t eltsize)
{
size_t size = count * eltsize;
void *value = malloc (size);
if (value != 0)
memset (value, 0, size);
return value;
}
void * realloc (void *ptr, size_t newsize)
作用:用来对之前申请的动态内存重新进行分配大小(扩容或缩小)。
参数解释:ptr必须为之前通过malloc或calloc等函数申请的动态内存首地址;newsize为你现在需要的大小。
注意事项:
1 如果是扩容,它不能保证后加的空间一定是接在原来申请的内存空间之后的,因为有可能其之后的空间已经被使用了。此时系统会找一块大小符合你要求的连续空间,并同时把原来空间内容复制到新空间,最后再释放原空间。
2 如果扩容失败,则原空间内容不受影响。
3 如果参数ptr为空,则其动作同malloc。
但是当我们使用时,为了程序的健壮性和完备性,在使用时我们需要包装一下:
void *
xrealloc (void *ptr, size_t size)
{
void *value = realloc (ptr, size);
if (value == 0)
fatal ("Virtual memory exhausted");
else
return value;
void * reallocarray (void *ptr, size_t nmemb, size_t size)
该函数作用及各参数同realloc,唯一的不同是当发生乘法溢出时,会进行错误提示。
void * aligned_alloc (size_t alignment, size_t size)
作用:函数分配一个地址alignment对齐的大小为size的内存空间。
参数解释:alignment代表地址对齐参数,必须为2的倍数,且size也必须是alignment的倍数。
如果分配成功则返回内存首地址,否则返回空,同时依据错误原因设置错误代码errno为ENOMEN或EINVAL