C 语言内存管理与动态内存分配
C 语言内存管理的基本概念,包括内存分区(代码区、数据区、堆区、栈区)及分配方式。详细讲解了动态内存分配函数 malloc、calloc、realloc 和 free 的用法与示例。同时分析了常见错误如内存泄漏、越界、重复释放及使用已释放内存,并提供了使用 Valgrind 工具检测与修复内存泄漏的方法。

C 语言内存管理的基本概念,包括内存分区(代码区、数据区、堆区、栈区)及分配方式。详细讲解了动态内存分配函数 malloc、calloc、realloc 和 free 的用法与示例。同时分析了常见错误如内存泄漏、越界、重复释放及使用已释放内存,并提供了使用 Valgrind 工具检测与修复内存泄漏的方法。

在 C 语言中,程序的内存可以分为以下几个区域:
内存的分配方式可以分为静态分配和动态分配:
内存的生命周期可以分为以下几个阶段:
malloc 函数用于在堆区动态分配内存。
函数原型:
void*malloc(size_t size);
功能:
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n * sizeof(int));
if(arr == NULL) {
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++) {
arr[i] = i + 1;
}
for(int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
执行结果:
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 5
分析:
calloc 函数用于在堆区动态分配内存,并将内存初始化为 0。
函数原型:
void*calloc(size_t nmemb,size_t size);
功能:
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)calloc(n,sizeof(int));
if(arr == NULL) {
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
执行结果:
arr[0] = 0 arr[1] = 0 arr[2] = 0 arr[3] = 0 arr[4] = 0
分析:
realloc 函数用于重新分配已动态分配的内存。
函数原型:
void*realloc(void*ptr,size_t size);
功能:
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
int new_n = 10;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++){
arr[i]= i +1;
}
arr =(int*)realloc(arr, new_n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = n; i < new_n; i++){
arr[i]= i +1;
}
for(int i =0; i < new_n; i++){
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
执行结果:
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 5 arr[5] = 6 arr[6] = 7 arr[7] = 8 arr[8] = 9 arr[9] = 10
分析:
free 函数用于释放已动态分配的内存。
函数原型:
voidfree(void*ptr);
功能:
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++){
arr[i]= i +1;
}
for(int i = 0; i < n; i++){
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
执行结果:
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 5
分析:
内存泄漏是指程序在动态分配内存后,没有及时释放内存,导致内存无法再使用。
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++){
arr[i]= i +1;
}
// 没有释放内存
return 0;
}
分析:
内存越界是指访问超出已分配内存范围的内存空间。内存越界会导致程序崩溃或被攻击者利用。
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i <= n; i++){
arr[i]= i +1;
}
for(int i = 0; i <= n; i++){
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
执行结果:
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 5 arr[5] = 6
分析:
重复释放内存是指对已释放的内存进行再次释放。重复释放内存会导致程序崩溃或被攻击者利用。
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++){
arr[i]= i +1;
}
free(arr);
free(arr);
return 0;
}
执行结果:
Segmentation fault (core dumped)
分析:
使用已释放的内存是指对已释放的内存进行访问。使用已释放的内存会导致程序崩溃或被攻击者利用。
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++){
arr[i]= i +1;
}
free(arr);
for(int i = 0; i < n; i++){
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
执行结果:
arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4 arr[4] = 5
分析:
可以使用一些工具来检测内存泄漏,如 Valgrind。
Valgrind 的安装:
sudo apt-get install valgrind # Ubuntu/Debian 系统
brew install valgrind # macOS 系统
使用 Valgrind 检测内存泄漏:
valgrind --leak-check=yes program
示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++){
arr[i]= i +1;
}
// 没有释放内存
return 0;
}
执行结果:
==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12345== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==12345== Command: program
==12345==
==12345== HEAP SUMMARY:
==12345== in use at exit: 20 bytes in 1 blocks
==12345== total heap usage: 1 allocs, 0 frees, 20 bytes allocated
==12345==
==12345== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345== by 0x108688: main (program.c:8)
==12345==
==12345== LEAK SUMMARY:
==12345== definitely lost: 20 bytes in 1 blocks.
==12345== indirectly lost: 0 bytes in 0 blocks.
==12345== possibly lost: 0 bytes in 0 blocks.
==12345== still reachable: 0 bytes in 0 blocks.
==12345== suppressed: 0 bytes in 0 blocks.
==12345==
==12345== For counts of detected and suppressed errors, rerun with: -v
==12345== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
分析:
修复内存泄漏的方法是使用 free 函数释放已动态分配的内存。
修复后的示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int* arr;
int n = 5;
arr = (int*)malloc(n *sizeof(int));
if(arr ==NULL){
printf("内存分配失败!\n");
return -1;
}
for(int i = 0; i < n; i++){
arr[i]= i +1;
}
free(arr);
return 0;
}
执行结果:
==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12345== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==12345== Command: program
==12345==
==12345== HEAP SUMMARY:
==12345== in use at exit: 0 bytes in 0 blocks
==12345== total heap usage: 1 allocs, 1 frees, 20 bytes allocated
==12345==
==12345== All heap blocks were freed -- no leaks are possible
==12345==
==12345== For counts of detected and suppressed errors, rerun with: -v
==12345== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
分析:
通过本文的学习,我们掌握了 C 语言内存管理与动态内存分配的方法,包括动态内存分配函数(malloc、calloc、realloc、free)的使用方法,以及动态内存分配导致的错误(内存泄漏、内存越界、重复释放内存、使用已释放的内存)。我们还学习了如何使用工具检测内存泄漏,并修复内存泄漏。
在编写代码时,我们应该注意动态内存分配导致的错误,使用正确的动态内存分配函数,并及时释放已动态分配的内存。同时,我们应该掌握内存泄漏的检测与修复方法,提高程序的正确性和可靠性。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online