面试过C++的同学都知道,面试官最喜欢问的问题之一就是C++内存模型问题。
例如static 、堆、栈等
网上对这方面的文章很多,但很多都是互相转发。并没有真正验证过。
下面我们通过真实代码来分析C++中,带static的变量的内存布局。
背景:我们知道,在c++中, static 可以用在几个地方:
1)函数修饰变量
2)修饰全局变量
3)类中,修饰类的静态成员变量
4)在类中,修饰表态函数
5)函数定义开头,修饰函数
问题1:函数中static 变量的内存是如何的?
#include "Apple.hpp"
static int g_a = 32;
static int g_b = 32;
static int g_c;
void test(){
int a = 3;
int b = 4;
int c = 3;
int d = 4;
printf("局部变量 a,b,c,d \n%p %p %p %p\n",&a,&b,&c, &d);
static int sa = 32;
static int sb = 32;
static int sc = 32;
static int sd;
printf("函数中的 static 变量 \n:sa,sb,sc,sd \n%p %p %p %p\n",&sa,&sb,&sc, &sd);
printf("全局 static 变量 g_a g_b g_c:\n%p %p %p\n",&g_a,&g_b,&g_c);
}
void test1(){
}
int main(int argc, const char * argv[]) {
// insert code here...
test();
};
输出结果:
0x7fff5fbff7bc 0x7fff5fbff7b8 0x7fff5fbff7b4 0x7fff5fbff7b0
函数中的 static 变量
:sa,sb,sc,sd
0x1000020ac 0x1000020b0 0x1000020b4 0x1000020c0
全局 static 变量 g_a g_b g_c:
0x1000020b8 0x1000020bc 0x1000020c4
分析:
//1. g_a, g_b的空间地址是连续的,相差为4,正好和int所占内存空间一致
//2. 地址是递增的。
static int g_a = 32; //0x1000020b8
static int g_b = 32;//0x1000020bc
static int g_c;//0x1000020c4
void test(){
//从a,b,c,d 的空间地址看出,函数的局部变量是存放在栈上,并且空间地址是递减的,这也符合栈空间的特点(众所周知,栈空间逆向生长)。
int a = 3;//0x7fff5fbff7bc
int b = 4;//0x7fff5fbff7b8
int c = 3;//0x7fff5fbff7b4
int d = 4;//0x7fff5fbff7b0
//1. g_a, g_b,g_c的空间地址是连续递增,相差为4,正好和int所占内存空间一致
//2. sd地址断开了
static int sa = 32;//0x1000020ac
static int sb = 32;//0x1000020b0
static int sc = 32;//0x1000020b4
static int sd; //0x1000020c0
把上面结果的static 变量按地址递增排序为:
sa< sb< sc < g_a < g_b < sd < g_c
可以得出两个结论:
1)函数中的static变量是放到和全局变量个内存区的,也就是全局内容区。
2)编译器给这些static 或 全局变量在内存中的位置为:先函数,再全局。先有值的变量,再无初始值的变量。
问题2:类中的static变量内存是如何分配的
为了搞清楚这个问题,我们先定义一个类Apple
头文件
class Apple{
public:
static int m_static_num;
};
实现
int Apple::m_static_num = 3;
测试
static int g_a = 32;
static int g_b = 32;
static int g_c ;
int g_d = 3;//新增加
void test(){
int a = 3;
int b = 4;
int c = 3;
int d = 4;
printf("局部变量 a,b,c,d \n%p %p %p %p\n",&a,&b,&c, &d);
static int sa = 32;
static int sb = 32;
static int sc = 32;
static int sd;
printf("函数中的 static 变量 \n:sa,sb,sc,sd \n%p %p %p %p\n",&sa,&sb,&sc, &sd);
printf("全局 static 变量 g_a g_b g_c g_d:\n%p %p %p %p\n",&g_a,&g_b,&g_c,&g_d);
}
void test1(){
}
int main(int argc, const char * argv[]) {
// insert code here...
test();
Apple apple = Apple();
printf("\napple object address %p , apple member address %p\n",&apple,&apple.m_static_a);
}
输出结果:
注意:以下结果,原全局变量,static变量的地址变化了。是因为原来代码中有些其他未列出的变量。单独分析这次结果不影响。
局部变量 a,b,c,d
0x7fff5fbff7ac 0x7fff5fbff7a8 0x7fff5fbff7a4 0x7fff5fbff7a0
函数中的 static 变量
:sa,sb,sc,sd
0x10000101c 0x100001020 0x100001024 0x100001030
全局 static 变量 g_a g_b g_c:
0x100001028 0x10000102c 0x100001034
apple object address 0x7fff5fbff7c8 , apple member address 0x100001018
分析: 结论: 下一章节分析类,函数,指针在内存中的分布
m_static_num
1)类的static变量,函数中的static变量,无static的全局变量,带static的全局变量 都是放到同一个内存区,全局内存区。
2)全局变量的初始化顺序,按Bjane的说法是变量定义的先后顺序,不同文件的看编译器具体实现,不做要求。全局变量(包括static)在main函数之前初始化( 动态连接库的情况下是在连接的时候初始化),在main函数结束后清理。