在本教程中,您将学习如何有效地管理内存。您将涵盖 C 语言中内存分配的类型、动态内存分配在 C 语言中的重要性,以及动态分配内存的不同方法,例如使用 malloc、calloc、realloc 和 free。
内存管理是 C 语言编程的一个重要方面。在编写程序时,我们必须确保分配的内存足以存储相关变量,并且空间不会被垃圾字节占用。基本上,C 语言编程通过 3 种不同的方式管理内存。它们是
到目前为止,我们已经学习了以静态模式为变量分配内存空间,但这会导致空间滥用。假设我们要存储一个名字,我们声明了一个 100 个字符的字符串来存储它。如果我们存储名字“Tim Cook”,只使用了 8 个字符。如果能有一种根据用户输入的数据来分配空间的方法,那就太好了。幸运的是,C 语言确实支持这种空间管理方式,通常称为“动态内存分配”。
动态内存分配
动态内存分配是一种在程序运行时,通过在需要时分配或释放内存来手动管理内存的方式。每当调用这些函数时,它们会从堆(内存区域)中获取内存,并在不需要时释放内存,以便可以重用。主要有两个函数执行动态内存分配:calloc 和 malloc。
这里对它们做一个简短的介绍。
术语“malloc”代表“内存分配”。C 语言中的 Malloc 用于在运行时分配指定字节数的内存。
语法
malloc() 函数的原型如下
ptr = (int*) malloc(10 * sizeof(int));
现在,这一切都取决于整数的属性。如果整数包含两个字节,则分配的空间将为 20 字节。如果整数的大小为 4 字节,则将保留 40 字节的空间。现在我们将尝试通过一个相关的例子来理解这个概念。请注意,我们包含了一个库文件 stdlib.h,因为这些函数是在这个特定的头文件中定义的。
示例
#include <string.h>
#include <stdlib.h>
main()
{
char name[50];
char *define;
strcpy(name, "Thomas Edison ");
define = malloc(50* sizeof(char));
if (define == NULL)
{
fprintf(stderr, "Error - Memory allocation failed. \n");
}
else
{
strcpy(define, " Thomas Edison invented incandescent lights.");
}
printf("Name is %s\n", name);
printf("Invention : %s\n", define);
}
输出
Name : Thomas Edison Invention : Thomas Edison invented incandescent lights.
因此,程序将内存分配的完全控制权交给了程序员。他可以根据情况的需要将值“50”更改为 20 或 100。在这个例子中,我们包含了一个字符串头文件来操作字符串,您将在接下来的教程中详细学习。
C 语言支持的另一个动态分配内存的库函数是 calloc() 函数,它被认为比 malloc 更好。函数 “calloc” 意为 “连续分配”,即对相邻单元进行串行和顺序分配。
语法
此函数的原型声明是
pointer = (data_type*) calloc( memory_units, size_of_element);
这里 calloc() 接受两个参数
假设我们要为 10 个 float 单元分配空间。我们知道 float 变量占用 4 个字节的空间,因此 10 个单元意味着 40 个内存元素。所以它的内存分配看起来会是这样
pntr = (float*) calloc(10, sizeof(float));
示例
#include <stdio.h>
int main()
{
int n;
int *p;
int i;
printf("Enter the number of elements in array :\n");
scanf("%d", &n);
p = (int*) calloc(n, sizeof(int));
printf("\nEnter the elements in array :\n");
if (p == NULL)
{
printf("\n Error : Memory allocation failed");
exit(1);
}
else
{
for (i = 0; i < n; i++)
{
scanf("%d", p + i);
}
printf("The array is :\n");
for (i = 0; i < n; i++)
{
printf("%d\t", *(p + i));
}
}
printf("\n\nThe array in reversed order is:\n");
for (i = n - 1; i >= 0; i--)
{
printf("%d\t", *(p + i));
}
return 0;
}
输出
Enter the number of elements in array : 4 Enter the elements in the array : 2 5 6 9 The array is : 2 5 6 9 The array in reversed order is: 9 6 5 2
calloc 和 malloc 的主要区别在于,前者分配多个相同大小的内存块。而后者则分配一个单一的内存块。Calloc 函数总是将分配的内存位初始化为零,而 malloc 函数则不会,因此包含垃圾值。Calloc 函数需要两个参数,而 malloc 只需要一个参数来分配内存。
当一个程序执行完成时,可能会出现两种情况。一种是你可能不再需要已分配的空间,另一种是我们必须重新定义大小(即增加或减少)。为了在运行时满足这些目的,C 语言中有两个独立的函数,分别是 free 和 reallocate。
顾名思义,它重新分配用于数据存储的空间。换句话说,reallocate() 函数启用了修改先前分配的内存大小而不丢失旧数据的功能。
语法
realloc() 函数的原型是
pointer_name = realloc(pointer_name, new_size)
与 calloc() 类似,realloc() 也接受两个参数,一个参数提供先前使用 malloc() 或 calloc() 分配的内存的第一个字节,第二个参数指定新的大小(可以比原始大小更大或更小)。考虑以下情况
pntr = (float*) calloc(10, sizeof(float));
假设我们想将已分配内存的大小增加 5 个浮点数,我们需要做的是为额外的 5 个浮点数单位重新分配空间。我们知道浮点变量占用 4 个字节的空间,因此 10 个单位加上额外的 5 个单位意味着 (15*4 = 60) 个内存元素。因此,它的内存重新分配将如下所示
pntr = (float*) realloc(pntr, 15*sizeof(float));
示例:
下面是一个演示 realloc() 工作原理的例子
#include <stdio.h>
#include <stdlib.h>
main()
{
int *p,s,ns,i;
printf("Enter the size of the array :");
scanf("%d", &s);
p = (int*) malloc(s * sizeof(int));
for(i= 0; i < s; ++i)
{
printf("\nAddress of each element in array at index %d ==> %u ",i,p+i);
}
printf("\n\nEnter new size of the data: ");
scanf("%d", &ns);
p = realloc(p, ns);
for(i = 0; i < ns; ++i)
printf("\nAddress of each element in array at index %d ==> %u ",i,p+i);
}
输出
Enter the size of the array :6 Address of each element in array at index 0 ==> 1731824 Address of each element in array at index 1 ==> 1731828 Address of each element in array at index 2 ==> 1731832 Address of each element in array at index 3 ==> 1731836 Address of each element in array at index 4 ==> 1731840 Address of each element in array at index 5 ==> 1731844 Enter the new size of the data: 2 Address of each element in array at index 0 ==> 1731824 Address of each element in array at index 1 ==> 1731828
这个函数的工作是释放已分配的内存。实际上,由 calloc 或 malloc 等函数分配的内存在程序执行后不会自动变空。要显式地释放空间,我们只需调用 free() 函数,并将持有待释放地址的正确指针作为参数传入。
语法
该函数的原型是
free (pointer);
演示 free() 的示例
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
//creating memory allocation dynamically
ptr = (int*) malloc(sizeof(int));
printf("\n Memory allocated...");
*ptr = 100; //assigning value printf("\n ptr c *(ptr));
free(ptr);
printf("\n Memory is deallocated...\n "); printf("ptr c *(ptr));
return 0;
}
输出
Memory allocated... ptr c Memory is deallocated... ptr c
在这个例子中,最初使用 malloc() 函数动态分配内存,并为其分配一个整数值。之后,我们使用 free() 函数释放了内存。
使用动态分配的主要优点是避免内存浪费。动态内存分配提供了根据需求更改内存大小的灵活性,并且适用于用户事先不知道内存大小的情况。
动态分配提出的唯一限制是需要显式释放。这意味着用户必须特别注意管理内存,在不再需要时释放空间,否则可能导致内存泄漏。