gifty

2011年5月7日

全局/静态变量内存布局

测试程序:

#include

#include

using namespace std;

static int gs_num[53];

char g_buf1[190];

char g_buf2[232];

char g_buf3[] = "That's really a test!";

static char gs_buf1[233];

class Base

{

public:

Base()

{

memset(buf, 1, 100);

}

~Base()

{

}

private:

char buf[100];

};

Base g_base;

static Base gs_base;

void func()

{

static int s1[10];

static char s2[16] = "fs";

static int s3[20] = {15};

s1[10] = 3;

int a = s1[0]+ s2[0];

}

int main()

{

static int a[10] = {3};

static int b[10] = {4};

//func();

static Base base;

static char temp[100];

static char temp1[39];

static Base base1;

int c = a[0] + b[0];

cout<gs_buf1[0] = 'a';

return 0;

}

内存布局

在main入口处设置断点后内存布局如下:

0x8049160 : 1952540756 1914729255 1819042149 543236217

0x8049170 : 1953719668 33 0 0

0x8049180 <_ZZ4mainE1b>: 4 0 0 0

0x8049190 <_ZZ4mainE1b+16>: 0 0 0 0

0x80491a0 <_ZZ4mainE1b+32>: 0 0 0 0

0x80491b0: 0 0 0 0

0x80491c0 <_ZZ4mainE1a>: 3 0 0 0

0x80491d0 <_ZZ4mainE1a+16>: 0 0 0 0

0x80491e0 <_ZZ4mainE1a+32>: 0 0 29542 0

0x80491f0 <_ZZ4funcvE2s2+8>: 0 0 0 0

0x8049200 <_ZSt4cout@@GLIBCXX_3.4>: 3086816908 3086816928 6 0

0x8049210 <_ZSt4cout@@GLIBCXX_3.4+16>: 4098 0 0 0

0x8049220 <_ZSt4cout@@GLIBCXX_3.4+32>: 0 0 0 0

0x8049230 <_ZSt4cout@@GLIBCXX_3.4+48>: 0 0 0 0

0x8049240 <_ZSt4cout@@GLIBCXX_3.4+64>: 0 0 0 0

0x8049250 <_ZSt4cout@@GLIBCXX_3.4+80>: 0 0 0 0

0x8049260 <_ZSt4cout@@GLIBCXX_3.4+96>: 0 0 8 134517288

0x8049270 <_ZSt4cout@@GLIBCXX_3.4+112>: 3086833084 0 0 3086827968

0x8049280 <_ZSt4cout@@GLIBCXX_3.4+128>: 3086829728 3086830428 3086830420 0

0x8049290 : 0 0 0 0

0x80492a0 : 0 0 0 0

0x80492b0 : 0 0 0 0

0x80492c0 : 0 0 0 0

0x80492d0 : 0 0 0 0

0x80492e0 : 0 0 0 0

0x80492f0 : 0 0 0 0

0x8049300 : 0 0 0 0

0x8049310 : 0 0 0 0

0x8049320 : 0 0 0 0

0x8049330 : 0 0 0 0

0x8049340 : 0 0 0 0

0x8049350 : 0 0 0 0

0x8049360 : 0 0 0 0

0x8049370 : 0 0 0 0

0x8049380 : 0 0 0 0

0x8049390 : 0 0 0 0

0x80493a0 : 0 0 0 0

0x80493b0 : 0 0 0 0

0x80493c0 : 0 0 0 0

0x80493d0 : 0 0 0 0

0x80493e0 : 0 0 0 0

0x80493f0 : 0 0 0 0

0x8049400 : 0 0 0 0

0x8049410 : 0 0 0 0

0x8049420 : 0 0 0 0

0x8049430 : 0 0 0 0

0x8049440 : 0 0 0 0

0x8049450: 0 0 0 0

0x8049460 : 16843009 16843009 16843009 16843009

0x8049470 : 16843009 16843009 16843009 16843009

0x8049480 : 16843009 16843009 16843009 16843009

0x8049490 : 16843009 16843009 16843009 16843009

0x80494a0 : 16843009 16843009 16843009 16843009

0x80494b0 : 16843009 16843009 16843009 16843009

0x80494c0 : 16843009 0 0 0

0x80494d0: 0 0 0 0

0x80494e0 <_ZL7gs_base>: 16843009 16843009 16843009 16843009

0x80494f0 <_ZL7gs_base+16>: 16843009 16843009 16843009 16843009

0x8049500 <_ZL7gs_base+32>: 16843009 16843009 16843009 16843009

0x8049510 <_ZL7gs_base+48>: 16843009 16843009 16843009 16843009

0x8049520 <_ZL7gs_base+64>: 16843009 16843009 16843009 16843009

0x8049530 <_ZL7gs_base+80>: 16843009 16843009 16843009 16843009

0x8049540 <_ZL7gs_base+96>: 16843009 0 0 0

0x8049550 <_ZGVZ4mainE5base1>: 0 0 0 0

0x8049560 <_ZL7gs_buf1>: 0 0 0 0

0x8049570 <_ZL7gs_buf1+16>: 0 0 0 0

0x8049580 <_ZL7gs_buf1+32>: 0 0 0 0

0x8049590 <_ZL7gs_buf1+48>: 0 0 0 0

0x80495a0 <_ZL7gs_buf1+64>: 0 0 0 0

---Type to continue, or q to quit---

0x80495b0 <_ZL7gs_buf1+80>: 0 0 0 0

0x80495c0 <_ZL7gs_buf1+96>: 0 0 0 0

0x80495d0 <_ZL7gs_buf1+112>: 0 0 0 0

0x80495e0 <_ZL7gs_buf1+128>: 0 0 0 0

0x80495f0 <_ZL7gs_buf1+144>: 0 0 0 0

0x8049600 <_ZL7gs_buf1+160>: 0 0 0 0

0x8049610 <_ZL7gs_buf1+176>: 0 0 0 0

0x8049620 <_ZL7gs_buf1+192>: 0 0 0 0

0x8049630 <_ZL7gs_buf1+208>: 0 0 0 0

0x8049640 <_ZL7gs_buf1+224>: 0 0 0 0

0x8049650: 0 0 0 0

0x8049660 <_ZZ4mainE5base1>: 0 0 0 0

0x8049670 <_ZZ4mainE5base1+16>: 0 0 0 0

0x8049680 <_ZZ4mainE5base1+32>: 0 0 0 0

0x8049690 <_ZZ4mainE5base1+48>: 0 0 0 0

0x80496a0 <_ZZ4mainE5base1+64>: 0 0 0 0

0x80496b0 <_ZZ4mainE5base1+80>: 0 0 0 0

0x80496c0 <_ZZ4mainE5base1+96>: 0 0 0 0

0x80496d0: 0 0 0 0

0x80496e0 <_ZZ4mainE5temp1>: 0 0 0 0

0x80496f0 <_ZZ4mainE5temp1+16>: 0 0 0 0

0x8049700 <_ZZ4mainE5temp1+32>: 0 0 0 0

0x8049710: 0 0 0 0

0x8049720 <_ZZ4mainE4base>: 0 0 0 0

0x8049730 <_ZZ4mainE4base+16>: 0 0 0 0

0x8049740 <_ZZ4mainE4base+32>: 0 0 0 0

0x8049750 <_ZZ4mainE4base+48>: 0 0 0 0

0x8049760 <_ZZ4mainE4base+64>: 0 0 0 0

0x8049770 <_ZZ4mainE4base+80>: 0 0 0 0

0x8049780 <_ZZ4mainE4base+96>: 0 0 0 0

0x8049790: 0 0 0 0

0x80497a0 <_ZZ4funcvE2s1>: 0 0 0 0

0x80497b0 <_ZZ4funcvE2s1+16>: 0 0 0 0

0x80497c0 <_ZZ4funcvE2s1+32>: 0 0 0 0

0x80497d0: 0 0 0 0

0x80497e0: 0 0 0 0

0x80497f0: 0 0 0 0

0x8049800: 0 0 0 0

0x8049810: 0 0 0 0

0x8049820: 0 0 0 0

0x8049830: 0 0 0 0

0x8049840: 0 0 0 0

0x8049850: 0 0 0 0

分析结果:

全局变量与静态变量的唯一区别在于链接属性,全局变量为外部链接属性,全局静态变量(类内部的静态变量)为内部链接属性,函数内部的静态变量无链接属性。

注:对于类内部的静态变量,类名相当于一个命名空间,而全局静态变量的命名空间为::(全局命名空间),所以它们本质上无差别!

全局变量和静态变量内存布局基本相同,这里一并考虑。

全局/静态变量的内存分配主要是遵循一个大的原则,将初始化的和未初始化的变量分开存放,初始化的变量被放在全局数据区,未初始化的变量放在BSS段,这样有一个好处,BSS段在文件中是没有大小的,只有一个地址,所有未初始化的全局静态变量都指向这个地址,这样可以减小文件的大小。而在运行时,才会为BSS段分配内存空间,并且全部初始化为0,所以未初始化的全局/静态变量载入内存后,默认值为0。

然后我们看内存布局:

前面4个变量依次是,g_buf3,_ZZ4mainE1b,_ZZ4mainE1a和_ZZ4funcvE2s2,编译器在编译时为了防止名称冲突,会对变量函数名进行名称修饰,linux下可用c++filt工具进行还原。

还原后的变量依次是:

g_buf3,main函数中的静态变量b、a和func中的静态变量s2。

这4个是初始化了的全局/静态变量,所以被放在全局初始化区,这里我们得出以下几点结论。

1、可以发现s2和s3都被初始化了,可是只有s2被分配内存空间,而s3并未分配内存空间,这是因为linux中为静态变量分配内存是根据该变量是否被使用来判断的,如果一个静态变量定义后,未发现它被其它变量引用了,将不会为其分配内存空间。

2、对于全局变量,不管它是否被使用了,都会为其分配内存空间。

3、在函数中的静态变量,在内存中的顺序恰恰和它们在函数中的声明顺序相反(目前尚不知这样做的原因)。

这里有一个疑问,g_base和gs_base这两个变量被初始化了,为什么它们被放在未初始化的全局数据区。这是因为g_base和gs_base这两个变量是在运行时通过调用构造函数被初始化的,在编译时我们是无法知道它们的值的,所以在编译时它们也被放在了BSS段。所以这里得出结论:

4、全局/静态类变量在内存中被放在未初始化数据区。

下面我们看看未初始化数据区里面的变量,依次是

g_buf1、g_buf2、g_base、gs_base、main函数的base1、temp1、base,func函数的s1。

可以看到g_base、gs_base值不为0,而base1,base的值为0,因为我的断点是在main函数入口处设置的,所以全局类变量的构造函数已被调用过了,而main函数内的类变量的构造函数尚未调用,但是它们的内存空间已被分配,这印证了以上结论4。

posted @ 2011-05-07 15:52 gifty 阅读(844) | 评论 (0)编辑 收藏

2011年5月2日

GCC预编译头技术

最近在弄GCC的预编译头,用C++开发工程最难以忍受的就是乌龟似地的编译速度,用VC开发工程的时候,VC会默认帮我们引入预编译头,那么GCC呢?其实GCC也是支持预编译头得, http://lych.yo2.cn/articles/浅谈gcc预编译头技术.html 这篇文章就详细讲解了如何GCC下预编译头得一些知识, 所以具体如何在GCC中加入预编译头,大家可以参考这篇文章。

在给GCC添加预编译头时也遇到了一些问题,并且对C++/C的编译有了一些新的认识!


很多在linux下写程序的人都应该看过《和我一起写Makefile》这篇文章,里面讲过如何利用GCC的-MM选项自动生成依赖:


%.d: %.cpp
@$(GPP) -MM $(INCLUDE) $< > $@.tmp;\
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.tmp > $@;\
rmf $@.tmp


这里利用了一个模式规则,将CPP文件依赖的头文件写到一个.d文件中,执行Makefile时我们只需要include这些.d文件,GCC就会根据里面的依赖关系来生成对应的.o文件了!
但是实际上GCC的自动推导能力非常强大,我们其实只需要写下一条这样的模式就可以了,
%.o:%.cpp
@$(GPP) $(CPPFLAGS) $< $(INCLUDE)


GCC就会自动推导出CPP文件所需要依赖的头文件进行编译了!


如果你在自己的工程中加入了预编译头,你就必须使用下面一种模式。
举个例子,比如你们工程开发了一些基础库,已经比较稳定了,除了一些少量的BUG FIX外,很少需要修改,当你通过源码形式引用的时候,你当然不希望每次编译的时候都去编译
这些基础库,如是你决定将他们加入预编译头中去,但是当你使用第一种方式写Makefile的时候,你发现被你加入到预编译头中的头文件还是被重复编译了,因为-MM选项会把你CPP文件
依赖的非系统头文件全部放在对应的.d文件中。但是当你使用第二种Makefile时,GCC看到一个预编译头后,他不会按照常规的方式将它展开,而是回去寻找对应的.gch文件,然后进行编译,
如果你想一探究竟的话,编译加上-H选项就可以看出其中的差异了!

这样生成的预编译头如果要使用的话,需要和编译CPP文件使用相同的选项,否则后出现一些奇怪的编译错误,我的做法是写一个预编译头得模式:

%.h.gch:%.h
$(GPP) $(CPPFLAGS) $< $(INCLUDE)

posted @ 2011-05-02 15:50 gifty 阅读(1888) | 评论 (0)编辑 收藏

仅列出标题  
<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

导航

统计

常用链接

留言簿

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜