内存分布
程序的内存可以大致分为以下几个部分:
栈(Stack)
- 作用:用于存储局部变量和函数调用的相关信息,如函数的参数、局部变量、返回地址等。
- 特点:栈的分配和释放遵循后进先出(LIFO)原则。每次函数调用时,局部变量会被压入栈中,函数返回时,这些变量会被自动清理。
堆(Heap)
- 作用:用于动态分配内存。通过
new
或malloc
等操作申请内存,分配的内存区域会一直保留直到显式释放(通过delete
或free
)。 - 特点:堆内存大小只受到系统可用内存的限制,相比栈,堆的内存管理更加灵活,但也更容易出错(如内存泄漏和内存越界)。
全局/静态数据区(Data Segment)
- 作用:用于存储全局变量、静态变量和常量数据。这些变量的生命周期贯穿整个程序的执行。
- 特点:分为初始化和未初始化两部分
- 初始化区:存储已初始化的全局变量和静态变量。
- 未初始化区(BSS区):存储未初始化的全局变量和静态变量,系统会自动将它们初始化为0。
代码区(Text Segment)
- 作用:存储程序的机器代码(即指令)。这一部分是只读的,程序运行时不可修改。
- 特点:这部分内存中存储的是程序的二进制指令,程序的代码通常不占用大量内存,因此通常在程序启动时就被加载到内存中。
常量区(Constant Data Segment)
- 作用:存储程序中使用的常量数据,如字符串常量等。
- 特点:这部分通常是只读的,程序不能修改其中的内容。常量区一般与代码区共享一部分内存。
C语言动态内存分配
malloc
(Memory Allocation)
- 功能:用于分配指定大小的内存块。
- 语法:
void* malloc(size_t size);
- 参数:
size
为要分配的内存大小(字节数)。 - 返回值:返回一个指向分配内存的指针。如果分配失败,返回
NULL
。 - 特点:分配的内存没有初始化,内存中的数据是随机的。
注意:使用 malloc
后,必须检查返回值是否为 NULL
,以确保内存分配成功。
calloc
(Contiguous Allocation)
- 功能:用于分配内存并将所有字节初始化为零。
- 语法:
void* calloc(size_t num, size_t size);
- 参数:
num
:需要分配的元素个数。size
:每个元素的大小(字节数)。
- 返回值:返回一个指向分配内存的指针。如果分配失败,返回
NULL
。 - 特点:与
malloc
不同,calloc
会将分配的内存清零(每个字节的值为 0),确保初始化为零。
realloc
(Reallocation)
- 功能:用于重新调整已分配内存的大小。可以增加或减少内存的大小。
- 语法:
void* realloc(void* ptr, size_t size);
- 参数:
ptr
:指向已分配内存块的指针。size
:新的内存块大小(字节数)。
- 返回值:返回一个指向新分配内存块的指针。如果分配失败,返回
NULL
,且原始内存块不会被释放。 - 特点:如果内存块大小增加,
realloc
会尝试扩展现有内存块并保持原有数据。如果大小减少,realloc
会截断内存,并保留原有数据。注意,如果realloc
返回NULL
,原有内存块不会被释放,需要自己处理。
注意:
realloc
可以用来调整大小,但需要特别注意返回的指针,因为realloc
可能会重新分配一块新的内存,原始指针将失效。如果使用返回的新指针赋值给原指针,之前的内存会被正确释放。- 如果
realloc
返回NULL
,表示重新分配失败,原始内存不会被释放。
总结
函数 | 功能 | 初始化 | 参数 | 返回值 |
---|---|---|---|---|
malloc | 分配指定大小的内存 | 否 | size_t size | 分配内存的指针 / NULL |
calloc | 分配指定数量和大小的内存,并初始化为0 | 是 | size_t num, size_t size | 分配内存的指针 / NULL |
realloc | 调整已经分配的内存块的大小 | 否 | void* ptr, size_t size | 新内存块指针 / NULL |
小问题
void Test()
{
int* p1 = (int*)malloc(sizeof(int)); // 动态分配内存
free(p1); // 释放p1指向的内存
int* p2 = (int*)calloc(4, sizeof(int)); // 动态分配内存并初始化为0
int* p3 = (int*)realloc(p2, sizeof(int) * 10); // 调整p2的内存大小为10个整数
free(p3); // 释放p3指向的内存
}
问题 :是否需要 free(p2)
?
这个问题关键在于如何使用 realloc
。在调用 realloc(p2, sizeof(int) * 10)
时,realloc
会尝试重新分配内存,并返回一个新的指针 p3
。
- 如果
realloc
成功,返回的新指针会指向新分配的内存块,此时原来的p2
指向的内存会被释放(即p2
指向的内存被回收)。因此,原来的p2
不再需要free
。 - 如果
realloc
失败,它会返回NULL
,此时原来的内存仍然有效,且需要释放。