C 知识量:16 - 74 - 317
通常在选择了某个存储类别之后,根据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()函数进行释放,因此,free()是非常重要的。
只要进行了内存的动态分配,就一定记得要在有关内存使用完毕后,用free()进行释放,否则,极易导致内存耗尽,也就是内存泄露。
动态分配内存除了使用malloc()函数外,还可以使用calloc()函数,例如:
int * p; p = (int *) calloc(10, sizeof (int));
与mallco()一样,calloc()在ANSI标准之前返回指向char的指针;在ANSI标准之后,返回指向void的指针,实际使用时,应当使用强制类型转换运算符,就像上面那样。
calloc()由两个无符号整数作为参数:第1个参数是所需的存储单元数量;第2个参数是存储单元的大小(以字节为单位)。
calloc()还有一个特性,就是会把块中的所有位都设置为0。
calloc()分配的内存也需要使用free()函数来释放,方法与malloc()相同。
变长数组和调用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()释放内存。
可以认为程序把可用的内存分为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
从上面的内存地址数据可以清楚的看出,静态数据(包括最后的字符串字面量)、自动数据、动态分配数据各自占用不同的内存区域。
Copyright © 2017-Now pnotes.cn. All Rights Reserved.
编程学习笔记 保留所有权利
MARK:3.0.0.20240214.P35
From 2017.2.6