在C++中,虚拟内存分为代码段、数据段、BSS段、堆区、文件映射区以及栈区六部分。其中,代码段、数据段、BSS段为静态存储区,堆区、映射区、栈区为动态存储区。
代码段:包括只读存储区和文本区,其中只读存储区存储字符串常量,文本区存储程序的机器代码。
数据段:存储程序中已初始化的全局变量和静态变量。
BSS段:存储未初始化的全局变量和静态变量,以及所有被初始化为0的全局变量和静态变量。对于未初始化的全局变量和静态变量,程序运行main函数之前会统一清0,即未初始化的全局变量编译器会初始化为0。
堆区:调用new/malloc函数时在堆区动态分配内存,同时需要调用delete/free来手动释放申请的内存。当进程未调用malloc时是没有堆段区间的,只有调用malloc时才分配一个堆,并且在程序运行过程中可以动态增加堆的大小。地址增长方式为从低地址向高地址增加。分配小内存时,即小于128k时使用break指针来平移。当分配内存大于128k时,使用的是mmap(),在映射区分配。
映射区:存储动态链接库以及调用mmap函数时进行的文件映射。
栈区:使用栈空间存储函数的返回地址、参数、局部变量、返回值。地址增长方式为从高地址向低地址增长,在创建进程时会有一个最大栈大小,此最大栈大小与编译器有关,VS下为1M。但此大小可以在Linux下通过ulimit命令指定。
32位CPU可寻址4G线性空间,每个进程都有各自独立的4G逻辑地址,其中0~3G是用户态空间,3~4G是内核空间,不同进程相同的逻辑地址会映射到不同的物理地址中。
可借助以下代码来理解内存分区的概念:
#include <stdio.h>
#include <stdlib.h>
int k = 1;
int main()
{
int i = 1;
char *j;
static int m = 1;
char *n = "hello";
/*变量n位于栈上,其内容为一地址,指向位于文字常量区的"hello",此时"hello"在内存中只有一份拷贝;
而语句char a[] = "hello",则不同。此时,a是一个位于栈上的有6个元素的数组,
并将"hello"拷贝到它所占的内存中,此时"hello"有两份拷贝。*/
printf("栈区地址为:0x%x\n", &i);
j = (char*)malloc(2);
free(j);
printf("堆区地址为:0x%x\n", j);
printf("全局变量地址为:0x%x\n", &k);
printf("静态变量地址为:0x%x\n", &m);
printf("文字常量区地址为:0x%x\n", n);
printf("程序代码区地址为:0x%x\n", &main);
return 0;
}
const char* arr = "123";
//字符串123保存在常量区,const本来是修饰arr指向的值不能通过arr去修改,但是字符串123在常量区,本来就不能改变,所以加不加const效果都一样。
char* brr = "123";
//字符串123保存在常量区,这个与arr指针指向的是同一个位置,同样也不能通过brr去修改123的值
const char crr[] = "123";
//此时的123本来是在栈上的,但是编译器可能会做某些优化,将其放到常量区
char drr[] = "123";
//字符串123保存在栈区,可以通过drr去修改
C++里是怎么定义常量的?常量存放在内存的哪个位置?
常量在C++里定义就是一个top-level const加上对象类型,定义时必须初始化。对于局部对象,常量存放在栈区,对于全局对象,常量存放在全局/静态存储区。对于字符值常量,存放在常量存储区。