C

C 知识量:16 - 74 - 317

12.3 分配内存><

malloc()和free()- 12.3.1 -

通常在选择了某个存储类别之后,根据C语言已制定好的内存管理规则,将自动确定其作用域和存储期。除此之外,还可以使用库函数来分配和管理内存,这种方式更加灵活。

可以在程序运行时分配更多的内存,主要的工具就是malloc()函数,它接受一个参数,即所需的内存字节数。

malloc()函数会自动在内存中找到合适的空闲内存块,同时返回动态分配内存块的首字节地址,可以将该地址赋给一个指针变量,并使用该指针访问这块内存。如果malloc()函数分配内存失败,将会返回空指针。

malloc()的返回类型为void,相当于一个通用指针,在实际使用时,应当总是使用强制类型转换,以提高代码的可读性。使用方式如下:

int * p;
p = (int *) malloc(20 * sizeof (int));

以上代码为20个int类型的值请求内存空间,并设置p指向该位置。

free()函数用于释放malloc()分配的内存。通常,malloc()要与free()配套使用。

free()函数的参数是之前malloc()返回的地址。需要注意的是:不能用free()释放通过其他方式分配的内存。动态分配内存的存储期从调用malloc()分配内存到调用free()释放内存为止。

malloc()和free()的原型都在stdlib.h头文件中。如果内存分配失败,可以调用exit()函数结束程序,其原型也在stdlib.h中。此外,在stdlib.h中,还定义了两个exit()函数的返回值:EXIT_SUCCESS表示普通程序的结束;EXIT_FAILURE表示程序异常中止。

以下是一示例:

#include <stdio.h>
#include <stdlib.h> // 为 malloc()和free()函数提供原型

int main(void) {
    double * p;     //用于指向double类型数组
    int max;        //用于设置数组元素个数
    int number;
    int i = 0;
    puts("What is the maximum number of type double entries?");
    if (scanf("%d", &max) != 1) {
        puts("Number not correctly entered.");  //输入了错误的元素个数
        exit(EXIT_FAILURE);
    }
    p = (double *) malloc(max * sizeof (double));
    if (p == NULL) {
        puts("Memory allocation failed. Goodbye.");  //数组内存分配失败
        exit(EXIT_FAILURE);
    }
    // *p 现在指向有max个元素的数组
    puts("Enter the values for array(q to quit):");
    while (i < max && scanf("%lf", &p[i]) == 1)
        ++i;
    printf("Here are your %d entries:\n", number = i);
    for (i = 0; i < number; i++) {
        printf("%-5.2f ", p[i]);
        if (i % 5 == 4)
            putchar('\n');
    }
    if (i % 5 != 0)
        putchar('\n');
    puts("Done.");
    free(p);    //释放malloc()函数动态分配的内存
    system("pause");
    return 0;
}

运行结果为:

What is the maximum number of type double entries?
7
Enter the values for array(q to quit):
2.3
3.6
6.12
3.5
8.9
9.6
4.7
Here are your 7 entries:
2.30  3.60  6.12  3.50  8.90
9.60  4.70
Done.

以上代码中,free()函数位于程序的末尾,它释放了malloc()函数分配的内存。注意:free()函数只释放其参数指向的内存块。一些操作系统在程序结束时会自动释放动态分配的内存,但是有些系统不会,因此,建议总是使用free(),不要依赖操作系统来清理。

free()的重要性- 12.3.2 -

静态内存的数量在编译时是固定的,在程序运行期间也不会改变。自动变量使用的内存数量在程序执行期间自动增加或减少。但是动态分配的内存数量只会增加,除非用free()函数进行释放,因此,free()是非常重要的。

只要进行了内存的动态分配,就一定记得要在有关内存使用完毕后,用free()进行释放,否则,极易导致内存耗尽,也就是内存泄露。

calloc()函数- 12.3.3 -

动态分配内存除了使用malloc()函数外,还可以使用calloc()函数,例如:

int * p;
p = (int *) calloc(10, sizeof (int));

与mallco()一样,calloc()在ANSI标准之前返回指向char的指针;在ANSI标准之后,返回指向void的指针,实际使用时,应当使用强制类型转换运算符,就像上面那样。

calloc()由两个无符号整数作为参数:第1个参数是所需的存储单元数量;第2个参数是存储单元的大小(以字节为单位)。

calloc()还有一个特性,就是会把块中的所有位都设置为0。

calloc()分配的内存也需要使用free()函数来释放,方法与malloc()相同。

动态内存分配和变长数组- 12.3.4 -

变长数组和调用malloc()都可用于创建在运行时确定大小的数组,例如:

int n;
int * p;
scanf("%d", &n);
p = (int *) malloc(n * sizeof (int)); //p指向动态分配的int类型数组。
int array[n]; //int类型变长数组
p[2] = array[2] = 5;

以上代码中,分别使用malloc()函数和变长数组创建了在运行时才会确定元素个数的int类型数组,它们达到了相同的目的。

不同的是:变长数组是自动存储类型,因此,程序离开变长数组所在的块时,变长数组占用的空间会自动释放。而用malloc()函数创建的数组必须使用free()函数手动释放,但malloc()函数创建的数组不必局限在一个函数内访问。例如,可以在被调函数内使用malloc()创建数组,而通过malloc()返回的指针在主调函数内使用,并在末尾调用free()释放内存。

存储类别和内存分配- 12.3.5 -

可以认为程序把可用的内存分为3部分:

  • 一部分供具有外部链接、内部链接和无链接的静态变量使用。

  • 一部分供自动变量使用。

  • 一部分供动态内存分配。

1、静态存储类别所用的内存数量在编译时确定,只要程序还在运行,就可访问储存在该部分的数据。该类别的变量在程序开始执行时被创建,在程序结束时被销毁。

2、自动存储类别的变量在程序进入变量定义所在块时存在,在程序离开块时消失。因此,随着程序调用函数和函数结束,自动变量所用的内存数量也相应地增加和减少。这部分的内存通常作为栈来处理,新创建的变量按顺序加入内存,然后以相反的顺序销毁。

3、动态分配的内存在调用malloc()或相关函数时存在,在调用free()后释放。这部分的内存由程序员管理。内存块可以在一个函数中创建,在另一个函数中销毁。这部分的内存会显得支离破碎。因为,未使用的内存块会分散在已使用的内存块之间。另外,使用动态内存通常比使用栈内存慢。

程序会把静态对象、自动对象和动态分配的对象储存在不同的区域,以下是以示例:

#include <stdio.h>
#include <stdlib.h> /* 为 malloc()和free()函数提供原型 */
#include <string.h>

int static_number = 100;
const char * const_string = "Hello from const_string";

int main(void) {
    int auto_number = 200;
    char auto_string[] = "Hello from auto_string";
    int * p_number;
    char * p_string;
    p_number = (int *) malloc(sizeof (int));
    *p_number = 150;
    p_string = (char *) malloc(strlen("Hello from p_string") + 1);
    strcpy(p_string, "Hello from p_string");

    printf("static_number:%d at %p\n", static_number, &static_number);
    printf("auto_number:%d at %p\n", auto_number, &auto_number);
    printf("*p_number:%d at %p\n", *p_number, p_number);
    printf("%s at %p\n", const_string, const_string);
    printf("%s at %p\n", auto_string, auto_string);
    printf("%s at %p\n", p_string, p_string);
    printf("%s at %p\n", "Hello from God", "Hello from God");

    free(p_number);
    free(p_string);
    system("pause");
    return 0;
}

运行结果为:

static_number:100 at 0040a004
auto_number:200 at 0061ff24
*p_number:150 at 00b01300
Hello from const_string at 0040b064
Hello from auto_string at 0061ff0d
Hello from p_string at 00b01320
Hello from God at 0040b0c8

从上面的内存地址数据可以清楚的看出,静态数据(包括最后的字符串字面量)、自动数据、动态分配数据各自占用不同的内存区域。