C/C++内存分区和内存操作函数
在典型的 C++ 程序执行过程中,内存被大致划分为以下 4 个主要区域:
C++ 程序运行时的 4 个内存区域
区域 | 说明 | 生命周期 | 增长方向 |
---|---|---|---|
代码区 | 编译后的程序指令(程序的机器指令) | 程序整个运行期间 | 固定,靠近低地址,通常只读、共享 |
全局/静态区 | 全局变量、静态变量、常量 | 程序整个运行期间 | 固定,紧随代码区,也靠近低地址 |
堆区 | 动态内存分配(手动管理) | 动态分配的内存(new / malloc )手动分配与释放 |
低地址 → 高地址 |
栈区 | 局部变量、函数调用帧(自动管理) | 调用时生成、调用完释放(局部变量、函数参数、返回地址等) | 高地址 → 低地址 |
地址增长方向示意图
高地址
↓
+----------------------+
| 栈区(Stack) | ← 函数调用时局部变量在这里
+----------------------+
| |
| 空闲区 | ← 堆和栈之间可能还有未分配内存
| |
+----------------------+
| 堆区(Heap) | ← new/malloc 动态分配的数据
+----------------------+
| 静态/全局区(Data) | ← static/global 变量
+----------------------+
| 代码区(Text) | ← 编译后的指令
+----------------------+
↑
低地址
区域详解
1. 代码区(Text Segment)
- 包含程序的可执行指令(.text 段)
- 通常是只读的(防止程序自我修改)
- 可以多个进程共享(节省内存)
2. 全局/静态区(Data Segment / BSS Segment)
- 包含:
- 已初始化的全局变量、静态变量 →
.data
- 未初始化的全局/静态变量 →
.bss
- 字符串常量、只读常量 →
.rodata
- 已初始化的全局变量、静态变量 →
- 生命周期:整个程序运行期间都存在
3. 堆区(Heap)
- 程序运行时使用
new
/malloc
动态分配的内存 - 管理成本高(需手动释放或智能指针)
- 地址从低向高增长
4. 栈区(Stack)
- 函数调用产生的临时变量、参数、返回地址
- 自动释放
- 地址从高向低增长
- 受限(栈空间有限,过深递归会栈溢出)
实验:打印地址观察
#include <iostream>
int global_var = 1;
static int static_var = 2;
int main() {
int local_var = 3;
static int static_local = 4;
int* heap_var = new int(5);
std::cout << "Code (main): " << (void*)main << "\n";
std::cout << "Global var: " << &global_var << "\n";
std::cout << "Static var: " << &static_var << "\n";
std::cout << "Static local: " << &static_local << "\n";
std::cout << "Heap var: " << heap_var << "\n";
std::cout << "Stack var: " << &local_var << "\n";
delete heap_var;
return 0;
}
// 输出结果
Code (main): 0x5ef0cdcb9209
Global var: 0x5ef0cdcbc010
Static var: 0x5ef0cdcbc014
Static local: 0x5ef0cdcbc018
Heap var: 0x5ef0f2ed4eb0
Stack var: 0x7ffc537b7ebc
C语言内存分配和释放函数
在 C 语言中,动态内存管理是通过一组标准库函数来实现的,这些函数包括 malloc
、calloc
、realloc
和 free
。它们提供了对堆(heap)内存的手动分配和释放控制,这对于需要在运行时确定大小的数据结构或数组特别有用。下面详细介绍这四个函数的功能、用法及其区别。
1. malloc
-
功能:分配指定字节数的内存块,并返回指向该内存块起始位置的指针。
-
原型:
void *malloc(size_t size);
-
参数:
size
- 需要分配的内存大小(以字节为单位)。 -
返回值:成功时返回一个指向分配内存的指针;如果内存不足,则返回
NULL
。 -
注意事项:分配的内存不会被初始化,内容可能是随机的。
示例代码
int *ptr = (int *)malloc(5 * sizeof(int)); // 分配能存储5个整数的空间
if(ptr == NULL) {
printf("Memory allocation failed\n");
}
2. calloc
-
功能:分配并初始化指定数量和类型的元素组成的内存块。所有位均设置为零。
-
原型:
void *calloc(size_t num, size_t size);
-
参数:
num
- 要分配的元素数量。size
- 每个元素的大小(以字节为单位)。
-
返回值:成功时返回一个指向分配内存的指针;如果内存不足,则返回
NULL
。 -
注意事项:与
malloc
不同,calloc
会将分配的内存初始化为零。
示例代码
int *ptr = (int *)calloc(5, sizeof(int)); // 分配并初始化能存储5个整数的空间
if(ptr == NULL) {
printf("Memory allocation failed\n");
}
3. realloc
-
功能:调整之前已分配内存块的大小,可以增加也可以减少。
-
原型:
void *realloc(void *ptr, size_t size);
-
参数:
ptr
- 指向之前通过malloc
,calloc
, 或realloc
分配的内存块的指针。size
- 新的内存块大小(以字节为单位)。
-
返回值:成功时返回一个指向新分配内存的指针;如果内存不足,则返回
NULL
,但原内存保持不变。 -
注意事项:如果新的大小比原来的要小,数据可能被截断;如果增大了,额外的空间内容未定义。
示例代码
int *ptr = (int *)malloc(5 * sizeof(int));
// 假设我们需要更多的空间...
ptr = (int *)realloc(ptr, 10 * sizeof(int)); // 尝试重新分配更大的空间
if(ptr == NULL) {
printf("Memory reallocation failed\n");
}
4. free
-
功能:释放之前通过
malloc
,calloc
, 或realloc
分配的内存块。 -
原型:
void free(void *ptr);
-
参数:
ptr
- 指向要释放的内存块的指针。 -
注意事项:一旦调用了
free
,ptr
所指向的内存就不能再被访问。尝试这样做会导致未定义行为。此外,多次释放同一块内存也会导致错误。
示例代码
int *ptr = (int *)malloc(5 * sizeof(int));
// 使用 ptr ...
free(ptr); // 释放内存
ptr = NULL; // 设置为 NULL 是一个好的实践,防止悬挂指针
📝 总结
malloc
:简单地分配内存而不进行初始化。calloc
:分配内存并将其初始化为零。realloc
:用于调整已有内存块的大小,同时保留原有数据(如果有足够的空间)。free
:释放之前分配的内存,避免内存泄漏。
C语言内存拷贝与设置函数
在 C 语言中,以 mem
为前缀的函数属于 内存操作函数,它们定义在标准头文件 <string.h>
中(注意:不是 <memory.h>
,虽然某些系统支持)。这些函数用于对连续内存块进行操作,与字符串处理函数不同,它们不依赖于终止符 \0
,而是直接根据给定的字节数进行操作。参数是dest指针在最前,size_t在最后。
✅ 1. void *memcpy(void *dest, const void *src, size_t n);
- 功能:从
src
拷贝n
个字节到dest
。 - 注意:不能用于内存区域重叠的情况。如果源和目标内存区域有重叠,行为未定义。
- 返回值:返回指向
dest
的指针。
示例:
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello, World!";
char dest[20];
memcpy(dest, src, strlen(src) + 1); // 包括 '\0'
printf("dest: %s\n", dest);
return 0;
}
✅ 2. void *memmove(void *dest, const void *src, size_t n);
- 功能:与
memcpy
类似,但可以安全地用于内存区域重叠的情况。 - 返回值:返回指向
dest
的指针。
示例:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "memmove can overlap";
memmove(str + 7, str, 10); // 安全地复制重叠区域
printf("%s\n", str);
return 0;
}
✅ 3. int memcmp(const void *s1, const void *s2, size_t n);
- 功能:比较两个内存块的前
n
个字节。 - 返回值:
- 若
s1 < s2
返回负整数; - 若相等返回 0;
- 若
s1 > s2
返回正整数。
- 若
示例:
#include <stdio.h>
#include <string.h>
int main() {
char a[] = "abcde";
char b[] = "abXde";
int result = memcmp(a, b, 5);
if (result == 0)
printf("Equal\n");
else if (result < 0)
printf("a < b\n");
else
printf("a > b\n");
return 0;
}
✅ 4. void *memset(void *s, int c, size_t n);
- 功能:将
s
所指向的内存块的前n
个字节设置为值c
(以unsigned char
形式写入)。 - 用途:常用于初始化内存、填充数组或结构体。
示例:
#include <stdio.h>
#include <string.h>
int main() {
char buffer[10];
memset(buffer, 'A', 5); // 前5个字符设为 'A'
memset(buffer + 5, 0, 5); // 后5个设为 0(空字符)
for(int i = 0; i < 10; i++) {
printf("%d ", buffer[i]);
}
return 0;
}
✅ 5. void *memchr(const void *s, int c, size_t n);
- 功能:在内存块
s
的前n
个字节中查找值为c
(转换为unsigned char
)的第一个出现位置。 - 返回值:找到则返回指向该位置的指针;否则返回
NULL
。
示例:
#include <stdio.h>
#include <string.h>
int main() {
char data[] = {0x10, 0x20, 0x30, 0x40, 0x20};
void *p = memchr(data, 0x20, sizeof(data));
if(p != NULL)
printf("Found at position: %ld\n", (char*)p - data);
return 0;
}
总结
mem
开头的函数是 C 语言中非常基础且重要的内存操作工具:
memcpy
/memmove
:用于复制内存块;memcmp
:用于比较;memset
:用于初始化;memchr
:用于查找指定字节。
C++中的内存操作方式
1. 使用 C 标准库函数
C++ 兼容所有 C 的内存函数,只需包含头文件如 <cstdlib>
和 <cstring>
。
2. new
和 delete
运算符
new
int* p = new int;
int* arr = new int[10];
- 自动调用构造函数(对于类类型)。
- 分配失败时抛出异常(除非使用
nothrow
版本)。
delete
delete p;
delete[] arr; // 注意数组要用 delete[]
- 自动调用析构函数。
- 必须匹配
new
和delete
类型。
在现代 C++ 编程中,应优先使用以下方式管理内存:
1. 智能指针
#include <memory>
std::unique_ptr<int> p(new int(5)); // 自动释放
std::shared_ptr<int> sp = std::make_shared<int>(10);
2. 容器类
#include <vector>
std::vector<int> vec(10); // 自动管理内存
3. RAII 模式
资源获取即初始化(Resource Acquisition Is Initialization),确保资源安全释放。
未经作者同意请勿转载
本文来自博客园作者:aixueforever,原文链接:https://www.cnblogs.com/aslanvon/p/18927403