C++内存分布
内存分布#
典型的C/C++内存表示有以下几部分构成
- Text segment,存放代码段和只读常量的区域
- Initialized data segment,通常叫做数据段,已经初始化的静态变量和全局变量存放的区域,如static int i = 5、全局变量int j = 10都存放在数据段
- Uninitialized data segment(bss),未初始化的静态变量或者全局变量存放在bss(Block Started by Symbol)段,并且用0进行初始化。
- Heap,动态内存分配时数据存放的位置
- Stack,栈顶指针方向从自顶向下,与堆的方向相反,
参考:https://www.geeksforgeeks.org/memory-layout-of-c-program/
存储持续性、作用域、链接性#
这里引入3个概念:
- 存储持续性(storage duration)
- 作用域(scope)
- 链接性(linkage)
存储持续性#
存储持续性就是数据保存在内存中的时间,C++11中有四种方案来存储数据。
- 自动存储持续性,在函数定义中声明的变量(包括函数参数)的存储持续性为自动的。这些变量在函数或代码段开始时创建,执行完函数或代码块时,它们的内存被释放。
- 静态存储持续性,被static修饰的变量其内存在所有程序结束时才释放。
- 线程存储持续性(C++11增加),如果变量是使用关键字thread_local 声明的,则其生命周期与所属的线程一样长。
- 动态存储持续性,使用new运算符分配的内存一直存在,直到使用delete运算符将其释放或是程序结束。这种方式分配的内存,有时将其称为自由存储区或者是堆。
作用域、链接性#
作用域即变量所作用的范围
链接性是指变量与其他单元如何实现共享
c++有5种变量存储方式:
在声明变量中有存储说明符(storage class specifier)或CV-限定符(cv-qualifier)。
存储说明符:
- auto(C++11 表示自动类型判断)
- register(C+11 指出变量是自动的)
- static,变量不在任何函数中声明时链接性表示为内部链接性(即可以被文件内的所有单元访问),在某个函数中使用static声明时无链接性
- extern,其他文件访问某个文件中的某个变量必须使用extern来引用声明(reference declaration)
- thread_local(C++11新增)
- mutable,用它来指出,即使结构(或类)变量为const,其某个成员也可以被修改。
CV-限定符:
-
const,它表明,内存被初始化后,程序便不能再对它进行修改
-
volatile(不常用),关键字 volatile 表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化
#include <stdio.h>
int g; //全局变量,对应第四种存储方式
static int s_g = 5; //对应第五种存储方式
const int fingers = 10; //与static int finger = 10,一样其链接性表现为内部
extern const int f; //引用声明其他文件的变量f
void fun()
{
int i = 5; //局部变量,对应第一种存储方式
register int m = 0; //对应第二种存储方式
static int j = 0; //静态局部变量,对应第三种存储方式
....
}
int main() { ... }
动态内存分配#
C++使用new关键字进行动态内存分配(dynamic memory allocation),这一部分内存单元称为堆(heap)或是自由存储区(free storage)。new关键字有两种使用方法,一是常规new运算符,二是定位(placement)new运算符。定位new运算符是在指定进行内存的分配,常规new运算符则是在堆分配内存,举个例子:
#include <iostream>
#include <new> //使用new运算的特性,需要引进该文件
#include <cstring> //使用strcpy
struct chaff
{
char dross[20];
int slag;
};
const int size = 256;
char buff[size]; //缓冲区
int main()
{
const int N = 2;
//定位new运算符:指定内存单元位置
chaff *cf1 = new (buff) chaff[2]; //在buff缓冲区分配单元给cf1指针
//常规new运算符:在堆中分配内存单元
chaff *cf2 = new chaff[2]; //常规方法在堆开辟单元
char ds1[][20] = { "dross1", "dross2"};
char ds2[][20] = { "ndross1", "ndross2"};
std::cout << "缓冲区buff地址 " << (void*) buff << std::endl;
std::cout << "new(buff)的地址 " << cf1 << std::endl;
std::cout << "new的地址 " << cf2 << std::endl;
std::cout << std::endl;
for(int i = 0; i < N; i++)
{
strcpy(cf1[i].dross,ds1[i]); //字符串的复制
cf1[i].slag = i;
strcpy(cf2[i].dross,ds2[i]); //字符串的复制
cf2[i].slag = i + 2;
}
for(int i = 0; i < N; i++)
{
std::cout << cf1[i].dross << ' '
<< cf1[i].slag << std::endl;
std::cout << cf2[i].dross << ' '
<< cf2[i].slag << std::endl;
}
return 0;
}
输出结果:
全局变量buff数组在内存的bss段分配了空间,可以看到使用定位new运算符在buff所在内存单元进行内存的分配,而常规new运算符则是堆分配内存单元。
作者:qianxiaohan
出处:https://www.cnblogs.com/qianxiaohan/p/18174875
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!