动态内存管理
1.malloc
其原型是void *malloc(size_t size);
,它用来分配一个固定大小的内存空间,并返回一个指向这个内存空间的void类型指针,所以在使用它的返回值时,最好加上强制指针类型转换再赋值。注意,这个分配好的空间是位于内存的堆上。注意,不要用指向分配好的内存块的指针去指向其他变量,这会导致内存块地址的丢失,会导致内存泄漏。还有每次用完malloc函数,因为有可能分配内存失败,所以最好加上判断语句,里面写上一句exit()来保证程序的鲁棒性。
2.free
用来释放分配好的内存堆上的空间。注意这个命令只会释放内存的空间,并不会改变所释放的那个指针的值,但是强烈建议在释放指针后立马将它指向NULL!并且注意这个命令不能用来释放指向栈的指针,这会导致未知的行为。
在申请完内存空间后,良好的编程习惯是马上调用free释放内存空间,不然可能会导致内存泄漏等非常严重的问题。
所以用完malloc函数有几个标准步骤:
1.用判断语句检测分配内存是否成功。
2.用free函数释放内存空间。
3.将ptr指针指向NULL。
3.stdlib.h
exit()、free()、malloc()等函数都是在stdlib.h里,注意要用的时候加上相应的预处理命令添加stdlib.h。
4.size_t
size_t
其实就是无符号整形变量,在64位系统中是unsigned long int
。
5.关于ptr指针
int *ptr = NULL;
ptr = (int *)mallcoc(sizeof(int)*3);
在这段代码中,实际上可以把ptr指针当成是数组名,这个数组是一个长度为3的整型数组。可以使用ptr[i]
的形式来访问数组中的内容。
6.初始化内存空间
在string.h这个头文件中有一些函数可以用来初始化内存空间。例如memset
就是用常量来初始化一块内存地址。
memset(ptr, 0, N*sizeof(int));
表示把内存块全部初始为0,其中N*sizeof(int)
表示内存块的大小,要与之前分配的大小一致。
另外还有更简单的办法,使用calloc
函数,它与memlloc
的唯一不同时,分配内存的同时就会把内存块全部赋值为零。注意calloc
和malloc
函数的参数不同,calloc
有两个参数。
calloc(1024, sizeof(int));
malloc(1024*sizeof(int));
上面两句分配的内存空间大小是一样的。
7.关于string.h
问题:以 mem 开头的函数比如 memcpy,memcmp 被编入字符串标准库(函数的声明包含在 string.h),那么请问它们与同在该标注库的 strncpy,strcnmp 函数有什么区别呢?
从形式上看,str 开头的函数使用的是 char 类型的指针(char *)作为参数和返回值;而 mem 开头的函数使用的是 void 类型的指针(void *)作为参数和返回值。
从本质上看,str 开头的函数主要目的是提供字符串的拷贝,比较等操作;而 mem 开头的函数主要目的是提供一个高效的函数接口来处理内存空间的数据。
8.重新分配内存空间
memcpy()
函数可以把内存块1的内容复制到内存块2中,常用于内存块不够用的情况时,搬运旧内存块的内容到新内存块。注意在这种应用场景下要记得释放之前的那个内存块。
另外还有更简单的方法,使用realloc()
函数。它有两个参数,第一个参数是ptr指针,第二个是新空间的大小。注意,如果新空间的大小小于就空间那么可能导致数据丢失,慎用,良好的编程习惯都是,新空间一定要大于旧空间。在我看来,使用realloc函数可以非常高效的使用内存空间,因为非常能体现“动态”这一观点。
9.不能用函数初始化静态变量
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
static int *pi = (int *)malloc(sizeof(int));
*pi = 520;
printf("*pi = %d\n", *pi);
return 0;
}
这个程序会报错,初始化静态变量时不能调用函数。因为在调用main函数之前静态变量就完成了初始化,而调用calloc是在调用了main之后,所以是不可能用函数去初始化静态变量的。如果非要给静态变量分配一个内存块,只能在后面写上单独的一句话。如下:
……
static int *pi;
pi = (int *)malloc(sizeof(int));
……