【C语言】内存管理
文章目录
1.概念
C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。
在 C 语言中,内存是通过指针变量来管理的。指针是一个变量,它存储了一个内存地址,这个内存地址可以指向任何数据类型的变量,包括整数、浮点数、字符和数组等。C 语言提供了一些函数和运算符,使得程序员可以对内存进行操作,包括分配、释放、移动和复制等。
2.库函数
序号 | 函数及描述 |
---|---|
1 | void *calloc(int num, int size); 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为0。所以它的结果是分配了 num * size 个字节长度的内存空间,并且每个字节的值都是0。 |
2 | void free(void *address); 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 |
3 | void *malloc(int num); 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 |
4 | void *realloc(void *address, int newsize); 该函数重新分配内存,把内存扩展到 newsize 。 |
**注意:**void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。
3.动态分配内存
编程时,如果预先知道数组的大小,那么定义数组时就比较容易。例如,一个存储人名的数组,它最多容纳 100 个字符,所以您可以定义数组,如下所示:
c
char name[100];
但是,如果预先不知道需要存储的文本长度,例如想存储有关一个主题的详细描述。在这里,我们需要定义一个指针,该指针指向未定义所需内存大小的字符,后续再根据需求来分配内存,如下所示:
malloc
c
void *malloc(size_t size);
size 参数指定了需要分配的内存大小,单位是字节(byte)。size_t 是一个无符号整数类型,通常用来表示大小和计数。
功能描述:
malloc 函数在程序的堆区(heap)请求一块至少为 size 字节的内存区域。如果内存分配成功,malloc 返回一个指向这块内存的指针,程序员可以使用这个指针来访问和操作分配的内存。
如果分配失败,通常是因为堆上没有足够的内存空间,malloc 会返回 NULL。
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char name[100];
char *p;
strcpy(name, "xiao ming");
/* 动态分配内存 */
p = (char *)malloc( 10 * sizeof(char) );// 分配一个整数数组的空间,大小为10个char
if( p == NULL )
{
perror("malloc");
}else{
strcpy( p, "xiao ming is a student ");
}
printf("Name = %s\n", name );
printf("p: %s\n", p );
free(p);
}
注意事项:
- 初始化 :
malloc
分配的内存空间不会被自动初始化,它的初始内容是未定义的。如果需要初始化,必须手动设置。 - 内存泄漏 :使用
malloc
分配的内存必须在不再需要时使用free
函数释放。如果忘记释放,将导致内存泄漏。 - 大小计算 :分配数组时,需要使用
sizeof
来确保分配的内存大小正确。例如,分配一个包含n
个元素的数组时,应使用n * sizeof(element)
作为malloc
的参数。 - 错误处理 :如果
malloc
返回NULL
,应该妥善处理这种情况,可能是程序应该终止或采取其他恢复措施。 - 指针类型 :
malloc
返回的是void*
类型的指针,这是C语言中所有指针类型的通用类型。在使用malloc
分配特定类型的内存时,通常需要强制类型转换,如int* array = malloc(10 * sizeof(int));
。
malloc
是动态内存分配的核心函数之一,在需要处理不确定数量的数据或实现某些数据结构(如链表、树等)时非常有用。正确使用 malloc
和 free
对于编写稳健和高效的C程序至关重要。
calloc
calloc
是 C 语言中用于动态内存分配的另一个标准库函数,它在很多方面与 malloc
相似,但有一个关键的区别在于初始化行为。下面是 calloc
函数的详细介绍:
函数原型:
c
void *calloc(size_t num, size_t size);
num
参数指定了要分配的元素数量。size
参数指定了每个元素的大小,单位是字节。
功能描述:
calloc
函数在程序的堆区分配一块总大小为num * size
字节的内存区域,并且将这块内存区域的每个字节都初始化为0。这使得calloc
成为分配和初始化静态数组或数据结构(如矩阵)的理想选择。- 如果内存分配成功,
calloc
返回一个指向这块内存的指针;如果失败,返回NULL
。
使用示例:
c
#include <stdlib.h> // 包含calloc函数的头文件
int main() {
int rows = 5;
int columns = 5;
// 分配一个5x5的整数矩阵,并初始化为0
int *matrix = calloc(rows * columns, sizeof(int));
if (matrix == NULL) {
// 处理内存分配失败的情况
fprintf(stderr, "Memory allocation failed.\n");
return 1;
}
// 使用分配的内存
// 注意:矩阵已经初始化为0
free(matrix); // 释放分配的内存
return 0;
}
注意事项:
- 初始化 :与
malloc
不同,calloc
分配的内存区域会自动初始化为0,这对于需要清零的数据结构非常有用。 - 内存泄漏 :和
malloc
一样,使用calloc
分配的内存也必须在使用完毕后用free
函数释放,以避免内存泄漏。 - 大小计算 :分配数组或复合数据结构时,应使用
sizeof
来确保分配的内存大小正确。 - 错误处理 :如果
calloc
返回NULL
,应该妥善处理这种情况,可能是程序应该进行错误恢复或终止。 - 指针类型 :
calloc
返回的是void*
类型的指针,在使用时通常需要强制类型转换到具体的数据类型指针。
calloc
是动态内存分配中一个非常有用的函数,特别是当你需要分配一块内存并确保它是清零的状态时。正确地使用 calloc
可以帮助你更容易地初始化复杂的数据结构,并保持代码的清晰和简洁。
4.重新调整内存的大小和释放内存
当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议在不需要内存时,都应该调用函数 free() 来释放内存。
或者,可以通过调用函数 realloc() 来增加或减少已分配的内存块的大小。让我们使用 realloc() 和 free() 函数。
realloc
realloc
是 C 语言中的一个用于内存管理的函数,它允许你更改先前使用 malloc
或 calloc
函数分配的内存块的大小。这个函数非常有用,当你需要调整一个数据结构的大小以适应程序运行时变化的需求时。
函数原型:
c
void *realloc(void *ptr, size_t new_size);
ptr
是指向先前分配的内存块的指针。new_size
是新的内存大小,单位是字节。
功能描述:
realloc
尝试将指针ptr
指向的内存块调整为new_size
字节大小。- 如果
ptr
是NULL
,realloc
表现得就像malloc(new_size)
,分配一个新的内存块。 - 如果
new_size
是 0,realloc
表现得就像free(ptr)
,释放内存块。 - 如果
realloc
成功,它返回指向重新分配(可能移动)内存块的指针。如果内存块被移动,原内存块的内容会被复制到新位置。 - 如果
realloc
失败,它返回NULL
,原始内存块保持不变。
使用示例:
c
#include <stdlib.h> // 包含realloc函数的头文件
int main() {
int *array = malloc(5 * sizeof(int)); // 初始分配
if (array == NULL) {
fprintf(stderr, "Initial memory allocation failed.\n");
return 1;
}
// 假设需要扩大数组
array = realloc(array, 10 * sizeof(int)); // 重新分配更大的空间
if (array == NULL) {
fprintf(stderr, "Memory reallocation failed.\n");
free(array); // 确保释放原始内存
return 1;
}
// 使用扩大后的数组
// ...
free(array); // 释放分配的内存
return 0;
}
注意事项:
- 内存块可能移动 :
realloc
可能会将内存块移动到堆中的另一个位置,因此返回的新指针可能与原始指针不同。这意味着你应该总是使用realloc
返回的指针来引用内存。 - 内容复制 :如果内存块被移动,
realloc
会自动将原始内存块的内容复制到新位置。 - 内存泄漏风险 :如果
realloc
失败,原始内存块仍然有效,不会自动释放。因此,你应该在重新分配内存之前保存原始指针的副本,并在必要时释放它。 - 错误处理 :如果
realloc
返回NULL
,应该检查new_size
是否为预期值,并处理可能的错误情况。 - 性能考虑 :频繁地使用
realloc
可能会导致性能问题,特别是如果内存块经常需要移动到新位置时。
realloc
是一个强大的工具,可以帮助你管理程序运行时的内存需求。然而,由于它可能会改变内存块的位置,使用时需要小心,确保正确地处理返回的新指针。
free
free
函数是 C 语言中用于释放之前使用 malloc
、calloc
或 realloc
函数分配的动态内存的标准库函数。正确地使用 free
函数对于防止内存泄漏和有效管理程序的内存使用至关重要。
函数原型:
c
void free(void *ptr);
ptr
是指向要释放的内存块的指针。如果ptr
是NULL
,调用free
没有效果,但这种调用是安全的,不会引发错误。
功能描述:
free
函数的作用是将之前动态分配的内存块返回给操作系统或运行时环境,使其可以被其他部分的程序再次使用。- 当没有更多的内存可用于新的内存分配请求时,释放不再需要的内存块是一种良好的编程实践。
注意事项:
- 释放NULL指针 :根据 C 标准,尝试释放一个
NULL
指针是安全的,free
将不做任何操作。 - 悬挂指针:释放内存后,指针指向的内存已经不再属于你的程序,因此不应再使用该指针(称为悬挂指针)。
- 内存泄漏 :如果忘记了调用
free
来释放动态分配的内存,将导致内存泄漏,长期运行的程序可能会因此耗尽可用内存。 - 重复释放:不应尝试多次释放同一块内存,这可能会导致未定义的行为或程序崩溃。
使用 free
函数是 C 语言编程中的一个重要方面,它确保了程序的健壮性和资源的有效使用。记住,每次使用 malloc
、calloc
或 realloc
分配内存时,都应该在适当的时候使用 free
来释放内存。