malloc
函数从堆上分配一块内存,所分配的字节数由该函数唯一的参数指定,返回值是void
指针,如果内存不足,就会返回NULL
。此函数不会清空或者修改内存,所以我们认为新分配的内存包含垃圾数据。函数的原型如下:
void* malloc(size_t);
这个函数只有一个参数,类型是size_t
,我们在第1章讨论过此类型。传递参数给这个函数时要小心,因为如果参数是负数就会引发问题。在有些系统中,参数是负数会返回NULL
。
如果malloc
的参数是0,其行为是实现相关的:可能返回NULL
指针,也可能返回一个指向分配了0字节区域的指针。如果malloc
函数的参数是NULL
,那么一般会生成一个警告然后返回0字节。
以下是malloc
函数的典型用法:
int *pi = (int*) malloc(sizeof(int));
执行malloc
函数时会进行以下操作:
- 从堆上分配内存;
- 内存不会被修改或是清空;
- 返回首字节的地址。
注意 因为当
malloc
无法分配内存时会返回NULL
,在使用它返回的指针之前先检查NULL
是不错的做法,如下所示:int *pi = (int*) malloc(sizeof(int)); if(pi != NULL) { // 指针没有问题 } else { // 无效的指针 }
1. 要不要强制类型转换
C引入void
指针之前,在两种互不兼容的指针类型之间赋值需要对malloc
使用显式转换类型以避免产生警告。因为可以将void
指针赋值给其他任何指针类型,所以就不再需要显式类型转换了。有些开发者认为显式类型转换是不错的做法,因为:
- 这样可以说明
malloc
函数的用意; - 代码可以和C++(或早期的C编译器)兼容,后两者需要显式的类型转换。
如果没有引用malloc
的头文件,类型转换可能会有问题,编译器可能会产生警告。C默认函数返回整数,如果没有引用malloc
的原型,编译器会抱怨你试图把int
赋值给指针。
2. 分配内存失败
如果声明了一个指针,但没有在使用之前为它指向的地址分配内存,那么内存通常会包含垃圾,这往往会导致一个无效内存引用的错误。考虑如下代码片段:
int *pi;
...
printf("%d\n",*pi);
内存分配如图2-5所示。这个问题会在第7章详细讨论。
图2-5:没有分配内存
执行这段代码可能会导致一个运行时异常。字符串中这类问题比较常见,如下所示:
char *name;
printf("Enter a name: ");
scanf("%s",name);
这里使用的是name
所引用的内存,看起来似乎可以正确执行,实际上这块内存还没有分配。把图2-5中的pi
换成name
就可以说明这个问题。
3. 没有给malloc
传递正确的参数
malloc
函数分配的字节数是由它的参数指定的,在用这个函数分配正确的字节数时要小心。比如说要为10个双精度浮点数分配空间,那就需要80字节,通过下面的代码可以做到:
double *pd = (double*)malloc(NUMBER_OF_DOUBLES * sizeof(double));
注意 为数据类型分配指定字节数时尽量用
sizeof
操作符。
下例尝试为10个双精度浮点数分配内存:
const int NUMBER_OF_DOUBLES = 10;
double *pd = (double*)malloc(NUMBER_OF_DOUBLES);
这段代码实际只分配了10字节。
4. 确认所分配的内存数
没有标准的方法可以知道堆上分配的内存总数,不过有些编译器为此提供了扩展。另外,也没有标准的方法可以知道堆管理器分配的内存块大小。
比如说,如果我们为一个字符串分配64字节,堆管理器会分配额外的内存来管理这个块。所分配内存的总大小,以及堆管理器所用到的内存数,是两者的和。动态分配内存对此有说明。
malloc
可分配的最大内存是跟系统相关的,看起来这个大小由size_t
限制。不过这个限制可能受可用的物理内存和操作系统的其他限制所影响。
执行malloc
时应该分配所请求数量的内存然后返回内存地址。如果操作系统采用“惰性初始化”策略直到访问内存才真正分配的话会怎样?这时候万一没有足够的内存用来分配就会有问题,答案取决于运行时的操作系统。开发者一般不需要处理这个问题,因为这种初始化策略非常罕见。
5. 静态、全局指针和malloc
初始化静态或全局变量时不能调用函数。下面的代码声明一个静态变量,并试图用malloc
来初始化:
static int *pi = malloc(sizeof(int));
这样会产生一个编译时错误消息,全局变量也一样。对于静态变量,可以通过在后面用一个单独的语句给变量分配内存来避免这个问题。但是全局变量不能用单独的赋值语句,因为全局变量是在函数和可执行代码外部声明的,赋值语句这类代码必须出现在函数中:
static int *pi;
pi = malloc(sizeof(int));
注意 在编译器看来,作为初始化操作符的
=
和作为赋值操作符的=
不一样。
评论前必须登录!
注册