C++内存结构与static

本文详细解析了32位系统中程序的内存布局,包括4GB虚拟内存的分配原则,栈区、堆区、全局区/静态区以及程序代码区的功能与特性。深入探讨了各内存区域的数据存储方式、分配机制及其对程序运行的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个程序启动,操作系统为程序提供4GB的虚拟内存,用一个32位2进制数来表示这片内存。(并不是4GB都供用户访问,后2GB及前2GB的部分不能访问,0x00000000不能访问)

2^32=4 294 967 296=4*1024(G)*1024(M)*1024(K)
0x00000000(低地址)–0xFFFFFFFF(高地址),共2^32个地址,地址加1,对应下一个字节。每个地址的容量是1Byte,所以32位二进制数可表示4GB内存。

在这里插入图片描述

1、栈区(stack)
编译器自动分配释放内存,存放程序临时创建的局部变量,函数的参数、函数返回值等。分配内存时分配一块连续的内存区域,如果栈内空间小于所申请的空间大小,那么这时系统将揭示栈溢出,并给出相应的异常信息。栈由高位地址往低位地址扩展。

     int main()
{
	int array[3] = { 1,2,3 };
	cout << &array[0] << "  " << &array[1] << "  " << &array[2] << endl;
	int a1 = 0, a2 = 0, a3 = 0;
	cout << "a1:" << &a1 << "  a2: " << &a2 << "  a3: " << &a3 << endl;
	system("pause");
	return 0;

}

在这里插入图片描述
系统分配一块连续的内存存储局部变量的值,由高位地址向低位地址扩展。数组先存array[2]最后存array[0],大端模式。数组元素地址相差刚好4个字节;
连续定义的int 变量之间地址相差值大于4个字节(6byte),可能是编译器预留一些地址作他用。

2、堆区(heap)
用于存放进程运行中动态分配的内存段,它的大小并不固定,可动态扩张或缩减。由程序员手动申请内存(new)、释放内存(delete), 若程序员不释放,程序结束时可能由操作系统回收。类似于链表,不同区域的内存块通过指针链接起来的,在内存中不是一块连续的内存。一个节点从链中断开,就需要把断开的节点从内存中释放。由低位地址向高位地址扩展。

int main()
{
	int a1 = 0, a2 = 0, a3 = 0;
	cout << "&a1:" << &a1 << "  &a2 " << &a2 << "  &a3 " << &a3 << endl;
	int *p1 = new int(a1);//堆区
	int *p2 = new int(a2);
	int *p3 = new int(a3);
	cout << "&a1:" << p1 << "  &a2 " << p2 << "  &a3 " << p3 << endl;
	cout << "&p1:" << &p1 << "  &p2:" << &p2 << "  &p3:" << &p3 << endl;
	system("pause");
	return 0;
}

在这里插入图片描述
第二行a1,a2,a3的地址不连续,因为a1,a2,a3存放在堆中。
////// 没有按照从低地址向高地址扩展,还没找到原因。
第三行结果地址连续,int *p指针p本身是在栈中。

3、全局区/静态区(static)
程序编译阶段已经分配好内存空间并初始化,在程序整个运行期都存在。存放全局变量和静态变量。初始化的全局变量和静态变量在一块区域, 静态存储区内的变量若不显示初始化,则编译器会自动以默认的方式进行初始化,即静态存储区内不存在未初始化的变量。变量一经初始化不可修改。

static int s1=0, s2=0, s3=0;// 静态全局变量
int g1=0, g2=0, g3=0;//全局变量
int main()
{
	int a1 = 0, a2 = 0, a3 = 0;
	cout << "a1:" << &a1 << "  a2: " << &a2 << "  a3: " << &a3 << endl;
	int *p1 = new int(a1);
	cout << "a1:" << p1 << endl;
	cout << "s1:" << &s1 <<"  s2:"<< &s2 <<"  s3"<< &s3 << endl;
	cout << "g1:" << &g1 << "  g2:" << &g2 << "  g3:" << &g3 << endl;
	system("pause");
	return 0;
}

在这里插入图片描述
局部变量,从堆中动态分配内存的变量,静态常量(和全局变量)分别处于三个不同的内存段。

static int s1=0, s2=0, s3=0;// 静态全局变量
int g1=0, g2=0, g3=0;//全局变量
const int cc = 0;  //const全局变量
int main()
{
	const int ccc = 0;           //const 局部变量
	int a1 = 0;                        //局部变量
	static int s = 0;                 //静态局部变量
	cout << "a1:" << &a1 << endl;
	int *p1 = new int(a1);
	cout << "a1:" << p1 <<endl;
	cout << "g1:" << &g1 << "  g2:" << &g2 << "  g3:" << &g3 << endl;
	cout <<"s:"<< &s<< endl;
	cout << "const:" << &cc << endl;
	cout << "const1:" << &ccc << endl;
	system("pause");
	return 0;
}

在这里插入图片描述
全局const变量存放在静态区,局部const变量存放在栈区
总之,静态变量与全局变量存储在静态/全局区。
4、程序代码区
又称只读区。存放程序编译后的二进制码,这部分区域的大小在程序运行前就已经确定,通常属于只读。

其他问题:
动态区:函数调用时申请空间,函数调用结束后释放空间。
静态区:程序被加载时,系统分配空间,函数调用结束后分配的空间仍然存在。
静态存储区的变量具备持久性和默认值0。

static的作用:
(百度百科很全了)
注意:全局变量和全局静态变量的区别**

1)全局变量是不显式用static修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。

静态局部变量有以下特点:

该变量在全局数据区分配内存;
静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;

静态函数

在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。

静态数据成员

因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;
静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这 有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次, 则所有存款类对象的利息全改变过来了

静态成员函数

与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。
静态成员函数由于不是与任何的对象相联系,因此它不具有this指
针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。

### C++ 内存结构概述 C++ 程序的内存结构通常被划分为多个不同的区域,这些区域各自承担着特定的功能,并对程序的运行和性能产生影响。常见的内存区域包括栈(Stack)、堆(Heap)、静态存储区(Static Storage Area)以及常量存储区(Constant Storage Area)。这些区域在内存管理、生命周期和访问效率方面各有特点。 #### 栈(Stack) 栈区用于存放函数调用时的局部变量和参数值。这部分内存由编译器自动分配和释放,遵循后进先出(LIFO)的原则。栈的操作效率较高,因为其分配和释放都是通过移动栈指针实现的,不需要复杂的算法[^1]。 #### 堆(Heap) 堆区是程序员手动管理的内存空间,通常通过 `new` 和 `delete` 运算符进行动态内存分配释放。堆的分配方式较为灵活,但也容易导致内存泄漏或碎片化问题。如果程序员没有及时释放不再使用的内存块,操作系统可能会在程序结束时回收这些内存。 #### 静态存储区(Static Storage Area) 静态存储区用于存储全局变量和静态变量。这些变量的生命周期贯穿整个程序运行期间,因此它们的内存是在程序启动时分配的,并在程序结束时由系统自动释放。初始化的全局变量和静态变量通常位于一个连续的内存区域,而未初始化的全局变量和静态变量则位于另一个相邻的区域。 #### 常量存储区(Constant Storage Area) 常量存储区专门用于存储不可变的数据,例如字符串字面量和其他常量值。这类数据的生命周期同样贯穿整个程序运行过程,且不能被修改。程序结束后,这部分内存会由系统自动释放[^1]。 ### 自动变量(Automatic Variables) 自动变量是指在函数内部声明的局部变量,它们的生命周期仅限于该函数的作用域内。当函数被调用时,这些变量会被自动分配内存;当函数返回时,这些变量所占用的内存会被自动释放。这种机制使得栈区的内存管理非常高效。 ### 动态内存分配(Dynamic Memory Allocation) 动态内存分配是指在程序运行过程中,根据需要手动申请和释放内存的过程。C++ 中主要使用 `new` 和 `delete` 操作符来完成这一任务。动态内存分配允许程序在运行时根据实际需求调整内存使用情况,但同时也要求开发者具备良好的内存管理意识,以避免内存泄漏或悬空指针等问题[^2]。 ```cpp int* dynamicArray = new int[10]; // 分配一个包含10个整数的数组 // 使用数组... delete[] dynamicArray; // 释放数组 ``` 动态内存分配的一个重要特性是可以创建比静态分配更复杂的数据结构,如链表、树等。然而,它也带来了额外的责任——必须显式地释放所有已分配的内存。否则,程序可能会消耗过多内存资源,甚至导致崩溃[^3]。 ### 内存回收优化 当动态分配的内存不再需要时,必须通过 `delete` 或 `delete[]` 显式释放。释放后的内存可以重新用于其他分配请求。为了提高内存利用率并减少碎片化,现代内存管理系统通常会对空闲内存块进行合并操作。例如,当某个内存块被释放后,系统会检查其相邻的块是否也是空闲状态,如果是,则将它们合并为一个更大的空闲块[^3]。 此外,需要注意的是,在某些情况下,函数可能返回指向临时内存的指针。这种做法可能导致未定义行为,因为一旦函数返回,该内存可能已经被释放。以下是一个典型的错误示例: ```cpp char* f1() { char temp[] = "hello"; return temp; // 返回指向栈上局部变量的指针,存在风险 } ``` 上述代码中,`f1()` 函数返回了一个指向局部数组 `temp` 的指针。由于 `temp` 是自动变量,它的生命周期仅限于 `f1()` 函数内部。一旦函数返回,该数组的内存就会被释放,此时外部访问该指针会导致未定义行为[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值