C malloc函数

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函数时会进行以下操作:

  1. 从堆上分配内存;
  2. 内存不会被修改或是清空;
  3. 返回首字节的地址。

注意 因为当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章详细讨论。

enter image description here

图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));

注意 在编译器看来,作为初始化操作符的=和作为赋值操作符的=不一样。

赞(2)

评论 抢沙发

评论前必须登录!

 

C指针