1. 简介
C语言中有两种比较重要的内存组织形式,一种是栈,另一种是堆。
栈是系统管理的内存,通常是用于函数运行,从函数调用开始系统开辟一块栈内存,然后压入参数、返回值等一系列操作,函数结束后系统将这块内存回收,这不需要我们操作,都是由系统自动完成的。
堆是程序员所管理的内存,通常用于为全局变量申请内存,内存使用完成后,还要程序员手动释放。动态内存分配函数也是主要针对堆内存来使用的。
2. malloc()与free()
malloc()函数用于在堆内存中分配一块指定大小的内存,它定义在标准库头文件<stdlib.h>中,函数原型如下。
void* malloc(size_t size)
该函数接受一个size_t类型的整数作为参数,表示指定要分配的内存字节大小。返回值类型是一个void指针,指向分配好的内存的首地址,如果分配失败,则返回NULL。
malloc()函数可以为任意数据类型分配内存,一开始不知道要存储的该内存的数据是什么类型,因此返回值类型只能是void指针,然后通过强转成具体的数据类型。
int *p = (int*) malloc(sizeof(int));
上面的示例是一般写法,用于给int类型的数据分配内存,对于处理分配失败的情况,可以做个判断。
int *p = (int*) malloc(sizeof(int));
if(p == NULL){
printf("内存分配失败\n");
return 1;
}
free()函数用于释放malloc()等函数所动态分配的内存,将这块内存归还给系统,这需要程序员手动调用这个函数。它定义在标准库头文件<stdlib.h>中,函数原型如下。
void free(void* ptr)
该函数接受一个void指针类型的参数,用于指向所要释放的内存的地址。所分配的内存一旦释放后,就不能再操作这块内存,否则会造成不可预知的后果;也不能进行二次释放。
int* p = (int*) malloc(sizeof(int));
if(p == NULL){
printf("内存分配失败\n");
return 1;
}
...
free(p);
p = NULL;
释放内存后,指针变量还是会指向原来的地址,为了避免野指针,所以把指针设置为NULL。
3. 动态分配函数
3.1 calloc()
calloc()函数也是用于分配动态内存,它与malloc()函数的区别是:calloc()函数不仅会分配内存,还会将所分配的内存全都初始化为0。它定义在标准库头文件<stdlib.h>中,函数原型如下。
void* calloc(size_t nitems, size_t size);
该函数接受两个参数,第一个参数是nitems,表示某种数据类型元素的个数,第二个参数是size,表示每个元素的字节大小。该函数返回一个void类型的指针,指向所分配的内存;如果分配失败,则返回NULL。
int* p = calloc(10, sizeof(int));
if(p == NULL){
printf("内存分配失败");
}
free(p);
p = NULL;
注意:calloc()函数所分配的内存空间也需要调用free()函数手动释放。
3.2 realloc()
realloc()函数用于修改之前使用malloc()等函数已经分配过的内存,可进行大小调整。它定义在标准库头文件<stdlib.h>中,函数原型如下。
void* realloc(void* ptr, size_t size)
该函数接受两个参数,第一个参数是void类型的指针ptr,指向将要修改的内存块;第二个参数是size_t类型的szie,表示新的内存块的大小。该函数返回一个新的内存块的地址,如果分配失败,返回NULL。
int *p = malloc(sizeof(int) * 10);
p = realloc(b, sizeof(int) * 100);//修改内存大小
如果新的内存块大于原内存块,则不会对新增的内存进行初始化;如果新内存块小于原内存块,则会丢弃超出的内存。
如果第一个参数为NULL(空指针),则会分配一个新的内存块,那么和malloc()函数功能一样。如果第一个参数指向了已经分配的内存块且第二个参数为0的话,那么就会释放掉ptr所指向的内存块,并返回一个空指针NULL。
int *p = realloc(NULL, sizeof(int) * 100);
//等同于
int *p = malloc(sizeof(int) * 100);
//size为0
int *p = realloc(p, 0);//释放,返回空指针
3.3 memcpy()
memcpy()函数的作用是将一块内存的数据复制到另一块内存。它定义在标准库头文件<string.h>中,函数原型如下。
void *memcpy(void *dest,const void *source,size_t n)
该函数返回目的内存地址,即第一个参数;该函数接受三个参数:
- void *dest:目的内存地址;
- void *source:源内存地址;
- size_t n:复制的字节数。
memcpy()函数可以复制任何类型的数据,还可以指定要复制的大小。
const char source[50] = "https://www.peiqiblog.com";
char dest[50];
memcpy(dest, source, sizeof(source));
printf("%s\n", dest);
3.4 memcmp()
memcmp()函数用于比较两个内存块的内容。它定义在标准库头文件<string.h>中,函数原型如下。
int memcmp(const void *str1,const void *str2,size_t n);
该函数会逐字节进行比较两个内存块的内容,该函数接受三个参数。
- const void *str1:第一个内存块的指针;
- const void *str2:第二个内存块的指针;
- size_t n:指定比较的字节数。
该函数返回一个int类型的整数值,分三种情况。
- 两者相同:返回值为0;
- str1大于str2:返回一个正值;
- str1小于str2:返回一个负值。
char* s1 = "abcde";
char* s2 = "abccc";
memcmp(s1, s2, 3);
注意,memcmp()函数遇到空字符时不会停止,而是要根据指定的字节数来确定。