有趣的问题:空结构体的内存空间占用问题
2014-09-22 22:58
323 查看
原文地址:有趣的问题:空结构体的占用空间问题 作者:GFree_Wind
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
今天有一个网友问了我关于空结构体占用空间的问题。对于空结构体占用空间的问题,这个一早就知道,所以开始没有在意,直接就回答了。但是后来网友的提问下,还是发现一些有趣的事情。下面就来说说这个事情。
他的问题如下:
#include <stdio.h>
typedef struct empty {
} empty_t;
int main()
{
empty_t a, b;
printf("a address is 0x%X\n", &a);
printf("b address is 0x%X\n", &b);
if (&a == &b) {
printf("address is equal\n");
}
else {
printf("address is not equal\n");
}
return 0;
}
他当时问我,输出是相等还是不相等。我直接说不等,因为了解空结构体的问题。对于空结构体来说,尽管看上去它没有包含任何的成员变量,似乎占用空间应该为0。但是像上面的代码中定义empty_t a, b,如果empty_t占用0字节的话,那么a和b就会使用同一地址。这样的话,就会有冲突。所以对于空结构体来说,其大小一般为1字节或者4字节。——注意,这是我以前所知。所以,我就毫不犹疑的告诉他,输出为“not
equal”。
他告诉我结果是“not equal”,但是为什么前面输出的&a和&b的地址却是相等的,但是后面却是"not equal"呢?这一下勾起了我的兴趣,我又让他打印sizeof看看。结果他告诉我,linux上是0,windows上是1。现在看这个问题,是不是有点意思了啊?
我决定自己调试一下,先看了一下输出,确实如他所述
[xxx@xxx-vm-fc13 test]$ ./a.out
a address is 0xBFE20B10, size is 0
b address is 0xBFE20B10, size is 0
address is not equal
输出地址相同,size为0,但是后面却输出address is not equal。那么调试一下看看吧。
(gdb) p &a
$1 = (empty_t *) 0xbffff620
(gdb) p &b
$2 = (empty_t *) 0xbffff620
(gdb) p sizeof(a)
$3 = 0
(gdb) p sizeof(b)
$4 = 0
(gdb) p &a==&b
$5 = 1
前面的输出值都是跟程序输出的结果一样,但是在gdb中打印&a==&b,结果却是1,与程序输出结果不符。
看到这里,我大概就知道怎么回事了,于是使用反汇编来确定自己的想法。
1 (gdb) disassemble main
2 Dump of assembler code for function main:
3 0x080483f4 <+0>: push %ebp
4 0x080483f5 <+1>: mov %esp,%ebp
5 0x080483f7 <+3>: and $0xfffffff0,%esp
6 0x080483fa <+6>: sub $0x10,%esp
7 0x080483fd <+9>: mov $0x8048514,%eax
8 0x08048402 <+14>: movl $0x0,0x8(%esp)
9 0x0804840a <+22>: lea 0x10(%esp),%edx
10 0x0804840e <+26>: mov %edx,0x4(%esp)
11 0x08048412 <+30>: mov %eax,(%esp)
12 0x08048415 <+33>: call 0x8048314 <printf@plt>
13 0x0804841a <+38>: mov $0x8048534,%eax
14 0x0804841f <+43>: movl $0x0,0x8(%esp)
15 0x08048427 <+51>: lea 0x10(%esp),%edx
16 0x0804842b <+55>: mov %edx,0x4(%esp)
17 0x0804842f <+59>: mov %eax,(%esp)
18 0x08048432 <+62>: call 0x8048314 <printf@plt>
19 0x08048437 <+67>: nop
20 0x08048438 <+68>: movl $0x8048553,(%esp)
21 0x0804843f <+75>: call 0x8048324 <puts@plt>
22 0x08048444 <+80>: leave
23 0x08048445 <+81>: ret
24 End of assembler dump.
第8行是把sizeof(a)的结果0,赋给0x8(%esp),第9行是取0x10(%esp)的地址,赋给%edx。
第14行和第15行与之类似。所以无论是程序的输出还是gdb调试时候的打印结果,都表明a和b的地址相同,且sizeof都为0.
第20行 movl
$0x8048553,(%esp) 是这个问题的关键,这里没有任何的条件判断,也就是说gcc对代码进行了优化,它认为&a == &b为一个常量值,那么这个后面的条件语句已经是可以通过编译知道结果的。所以直接选择后面的语句执行,而去掉了条件判断。
那么让我们看看这个地址0x8048553是什么的地址吧。
(gdb) x /s 0x8048553
0x8048553 <__dso_handle+67>: "address
is not equal"
真相已经出来了。
下面总结一下:
1. 对于空结构体来说,linux与windows不同,空结构体的size确实当作0字节。——刚刚在windows验证了(VS2008),空结构体的size为1(但是我想肯定可以通过编译选项,将其对齐为4。不过我非windows程序员,就不研究这个了);
2. 对于这个问题,gcc在编译阶段进行了优化。对于空结构体来说,即使gcc把它的size作为0处理,但是定义两个两个变量时,仍然认为这两个变量的地址不同。——说实话,我觉得这一点处理上有些矛盾,不知道算不算gcc的bug呢;
3. 调试过程中,gdb是真正的去取a和b的地址,所以在gdb中&a==&b的值为1;
4. 对于这种有意思的代码行为,还是自己去调试一下,才能有收获。最好是可以在各个平台试验一下。
5. 在回答别人的问题时,对自己也是一种提高。因为每个人的思考方法是不同的,总会得到一些新东西,或者加深自己对问题的理解。
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
今天有一个网友问了我关于空结构体占用空间的问题。对于空结构体占用空间的问题,这个一早就知道,所以开始没有在意,直接就回答了。但是后来网友的提问下,还是发现一些有趣的事情。下面就来说说这个事情。
他的问题如下:
#include <stdio.h>
typedef struct empty {
} empty_t;
int main()
{
empty_t a, b;
printf("a address is 0x%X\n", &a);
printf("b address is 0x%X\n", &b);
if (&a == &b) {
printf("address is equal\n");
}
else {
printf("address is not equal\n");
}
return 0;
}
他当时问我,输出是相等还是不相等。我直接说不等,因为了解空结构体的问题。对于空结构体来说,尽管看上去它没有包含任何的成员变量,似乎占用空间应该为0。但是像上面的代码中定义empty_t a, b,如果empty_t占用0字节的话,那么a和b就会使用同一地址。这样的话,就会有冲突。所以对于空结构体来说,其大小一般为1字节或者4字节。——注意,这是我以前所知。所以,我就毫不犹疑的告诉他,输出为“not
equal”。
他告诉我结果是“not equal”,但是为什么前面输出的&a和&b的地址却是相等的,但是后面却是"not equal"呢?这一下勾起了我的兴趣,我又让他打印sizeof看看。结果他告诉我,linux上是0,windows上是1。现在看这个问题,是不是有点意思了啊?
我决定自己调试一下,先看了一下输出,确实如他所述
[xxx@xxx-vm-fc13 test]$ ./a.out
a address is 0xBFE20B10, size is 0
b address is 0xBFE20B10, size is 0
address is not equal
输出地址相同,size为0,但是后面却输出address is not equal。那么调试一下看看吧。
(gdb) p &a
$1 = (empty_t *) 0xbffff620
(gdb) p &b
$2 = (empty_t *) 0xbffff620
(gdb) p sizeof(a)
$3 = 0
(gdb) p sizeof(b)
$4 = 0
(gdb) p &a==&b
$5 = 1
前面的输出值都是跟程序输出的结果一样,但是在gdb中打印&a==&b,结果却是1,与程序输出结果不符。
看到这里,我大概就知道怎么回事了,于是使用反汇编来确定自己的想法。
1 (gdb) disassemble main
2 Dump of assembler code for function main:
3 0x080483f4 <+0>: push %ebp
4 0x080483f5 <+1>: mov %esp,%ebp
5 0x080483f7 <+3>: and $0xfffffff0,%esp
6 0x080483fa <+6>: sub $0x10,%esp
7 0x080483fd <+9>: mov $0x8048514,%eax
8 0x08048402 <+14>: movl $0x0,0x8(%esp)
9 0x0804840a <+22>: lea 0x10(%esp),%edx
10 0x0804840e <+26>: mov %edx,0x4(%esp)
11 0x08048412 <+30>: mov %eax,(%esp)
12 0x08048415 <+33>: call 0x8048314 <printf@plt>
13 0x0804841a <+38>: mov $0x8048534,%eax
14 0x0804841f <+43>: movl $0x0,0x8(%esp)
15 0x08048427 <+51>: lea 0x10(%esp),%edx
16 0x0804842b <+55>: mov %edx,0x4(%esp)
17 0x0804842f <+59>: mov %eax,(%esp)
18 0x08048432 <+62>: call 0x8048314 <printf@plt>
19 0x08048437 <+67>: nop
20 0x08048438 <+68>: movl $0x8048553,(%esp)
21 0x0804843f <+75>: call 0x8048324 <puts@plt>
22 0x08048444 <+80>: leave
23 0x08048445 <+81>: ret
24 End of assembler dump.
第8行是把sizeof(a)的结果0,赋给0x8(%esp),第9行是取0x10(%esp)的地址,赋给%edx。
第14行和第15行与之类似。所以无论是程序的输出还是gdb调试时候的打印结果,都表明a和b的地址相同,且sizeof都为0.
第20行 movl
$0x8048553,(%esp) 是这个问题的关键,这里没有任何的条件判断,也就是说gcc对代码进行了优化,它认为&a == &b为一个常量值,那么这个后面的条件语句已经是可以通过编译知道结果的。所以直接选择后面的语句执行,而去掉了条件判断。
那么让我们看看这个地址0x8048553是什么的地址吧。
(gdb) x /s 0x8048553
0x8048553 <__dso_handle+67>: "address
is not equal"
真相已经出来了。
下面总结一下:
1. 对于空结构体来说,linux与windows不同,空结构体的size确实当作0字节。——刚刚在windows验证了(VS2008),空结构体的size为1(但是我想肯定可以通过编译选项,将其对齐为4。不过我非windows程序员,就不研究这个了);
2. 对于这个问题,gcc在编译阶段进行了优化。对于空结构体来说,即使gcc把它的size作为0处理,但是定义两个两个变量时,仍然认为这两个变量的地址不同。——说实话,我觉得这一点处理上有些矛盾,不知道算不算gcc的bug呢;
3. 调试过程中,gdb是真正的去取a和b的地址,所以在gdb中&a==&b的值为1;
4. 对于这种有意思的代码行为,还是自己去调试一下,才能有收获。最好是可以在各个平台试验一下。
5. 在回答别人的问题时,对自己也是一种提高。因为每个人的思考方法是不同的,总会得到一些新东西,或者加深自己对问题的理解。
相关文章推荐
- [面试] 结构体占用空间的问题,内存对齐~! 真的懂了,cpu取加快速度,省空间来考虑。
- 结构体占用内存空间的问题
- [面试] 结构体占用空间的问题,内存对齐~! 真的懂了,cpu取加快速度,省空间来考虑。
- C++中结构体的内存空间占用
- C语言结构体占用空间内存大小解析
- 《C++中类对象的内存布局和占用空间》《C++ 类里面,函数占用存储空间问题 》
- 有如下CAT_s结构体定义,回答: 1) 在一台64位的机器上,使用32位编译,Garfield变量占用多少内存空间?64位编译又是如何?
- C语言结构体占用空间内存大小解析
- 《C++中类对象的内存布局和占用空间》《C++ 类里面,函数占用存储空间问题 》
- C语言结构体占用空间内存大小解析
- 结构体内存占用问题
- JAVA中引用本身占用内存空间的问题
- 面试中关于C++中的类,结构体,enum,字符变量等所占内存空间问题总结
- JAVA中引用本身占用内存空间的问题
- 【Python】小谈 numpy 数组占用内存空间问题
- 越深入java 就觉得java做得越烂,java设计问题 之 : 对象占用内存空间设计
- 关于结构体中变量空间占用问题思考
- 关于常用数据内存占用的常见问题(数据类型所占内存容量、大端小端、结构体联合体)
- JAVA中引用本身占用内存空间的问题
- VC中结构体内存分配问题透析