我们知道C语言当中结构可以使用{}进行初始化,例如有结构体定义如下:

typedef struct type_t {
int a;
int b;
int c;
int d;
}type_t;

我们可以使用下面的语句来初始化一个变量:

struct type_t o = { 1, 2, 3, 4 }

但是如果像下面这样来初始化,结果会是什么呢?

type_t o = { 8, o.a, o.b, o.c };

其结果是依赖于编译器的实现,笔者分别在VS2010和GCC下进行了测试,结果如下:

1. vs2010下C和C++的结果都是:

o.a = 8, o.b = 8, o.c = 8, o.d = 8

2. GCC的C编译运行结果:

o.a = 8 o.b = 0, o.c = 0, o.d = 4195296

3. GCC的C++编译运行结果:

o.a = 8, o.b = 8, o.c = 8, o.d = 8

为啥会这样呢?带着疑问,笔者又去看了一下反汇编的代码,VS2010的反汇编代码如下:

	type_t o = { 8, o.a, o.b, o.c };
004113A8 mov dword ptr [ebp-18h],8
004113AF mov eax,dword ptr [ebp-18h]
004113B2 mov dword ptr [ebp-14h],eax
004113B5 mov eax,dword ptr [ebp-14h]
004113B8 mov dword ptr [ebp-10h],eax
004113BB mov eax,dword ptr [ebp-10h]
004113BE mov dword ptr [ebp-0Ch],eax

这段代码翻译过来的意思就是:

o.a = 8;
eax = o.a
o.b = eax
eax = o.b
o.c = eax
eax = o.c
o.d = eax

gcc编译的反汇编代码:

=> 0x00000000004004cd <+9>:     mov    -0x20(%rbp),%ecx
0x00000000004004d0 <+12>: mov -0x1c(%rbp),%edx
0x00000000004004d3 <+15>: mov -0x18(%rbp),%eax
0x00000000004004d6 <+18>: movl $0x8,-0x20(%rbp)
0x00000000004004dd <+25>: mov %ecx,-0x1c(%rbp)
0x00000000004004e0 <+28>: mov %edx,-0x18(%rbp)
0x00000000004004e3 <+31>: mov %eax,-0x14(%rbp)

这段代码可翻译如下:

ecx=o.a
edx=o.b
eax=o.c
o.a=8
o.b=ecx
o.c=edx
o.d=eax

g++编译的反汇编代码:

=> 0x00000000004005ad <+9>:     movq   $0x0,-0x20(%rbp)
0x00000000004005b5 <+17>: movq $0x0,-0x18(%rbp)
0x00000000004005bd <+25>: movl $0x8,-0x20(%rbp)
0x00000000004005c4 <+32>: mov -0x20(%rbp),%eax
0x00000000004005c7 <+35>: mov %eax,-0x1c(%rbp)
0x00000000004005ca <+38>: mov -0x1c(%rbp),%eax
0x00000000004005cd <+41>: mov %eax,-0x18(%rbp)
0x00000000004005d0 <+44>: mov -0x18(%rbp),%eax
0x00000000004005d3 <+47>: mov %eax,-0x14(%rbp)

这段代码翻译如下:

将o变量清零
o.a = 8;
eax = o.a
o.b = eax
eax = o.b
o.c = eax
eax = o.c
o.d = eax

现在该完全清楚了,g++和vs2010生成的汇编指令基本一致,都是先将前一个成员初始化后再进行取值。而gcc则是先将o的成员变量值全部保存的寄存器中,然后逐一对o变量进行初始化。

但是所不明白的是为何gcc要这样生成指令?

一个C/C++结构体初始化有趣的现象的更多相关文章

  1. Linux C中结构体初始化

          在阅读GNU/Linux内核代码时,我们会遇到一种特殊的结构初始化方式.该方式是某些C教材(如谭二版.K&R二版)中没有介绍过的.这种方式称为指定初始化(designated in ...

  2. struct--------构造函数对结构体初始化的影响

    struct--------构造函数对结构体初始化的影响. 没有构造函数时使用如下: struct ClassBook{  int number;  int age; }; int main() { ...

  3. PAT A1075 PAT Judge (25 分)——结构体初始化,排序

    The ranklist of PAT is generated from the status list, which shows the scores of the submissions. Th ...

  4. Linux C 结构体初始化三种形式

    最近看linux代码时发现了结构体 struct 一种新的初始化方式,各方查找对比后总结如下: 1. 顺序初始化教科书上讲C语言结构体初始化是按照顺序方式来讲的,没有涉及到乱序的方式.顺序初始化str ...

  5. Linux下C结构体初始化[总结]

    1.前言 今天在公司看一同事写的代码,代码中用到了struct,初始化一个struct用的是乱序格式,如下代码所示: typedef struct _data_t { int a; int b; }d ...

  6. Linux下C结构体初始化

    1.前言 今天在公司看一同事写的代码,代码中用到了struct,初始化一个struct用的是乱序格式,如下代码所示: typedef struct _data_t { int a; int b; }d ...

  7. C语言结构体初始化方法

    早上苏凯童鞋问我这个问题来着,写在这里. 我了解到的C中结构体初始化的方法大概有三种. 如这里我定义了一个结构体: typedef struct node { int x, y; }Node; 第一种 ...

  8. golang | Go语言入门教程——结构体初始化与继承

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是golang专题第10篇文章,我们继续来看golang当中的面向对象部分. 在上一篇文章当中我们一起学习了怎么创建一个结构体,以及怎么 ...

  9. 【2016-08-18】转载:总结C++中几种结构体初始化的方法

    作者:Ac_Von 博客地址:http://www.cnblogs.com/vongang/ 文章地址:http://www.cnblogs.com/vongang/archive/2011/07/3 ...

随机推荐

  1. C语言的本质(18)——函数的可变参数

    一般而言,在设计函数时会遇到许多数学和逻辑操作,是需要一些可变功能.例如,计算数字串的总和.字符串的联接或其它操作过程. 实现一个函数,要求在函数中计算传入的所有参数之和,并输出到屏幕上.这个函数实现 ...

  2. #include <boost/scoped_array.hpp>

    多个元素使用#include <boost/scoped_array.hpp> 单个元素使用#include <boost/scoped_ptr.hpp> 作用域数组 作用域数 ...

  3. IE8对css文件的限制

    很多人在写css时,时常把很多css样式放到一个文件中.也有些框架在上线后,能对很多css文件进行合并.这样能减少对服务器的请求次数,从而加快服务器的响应速度.在IE8中,当css的规则个数大于409 ...

  4. OC基础10:基本的C语言特性1

    "OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.  函数和方法的区别? (1).方法包 ...

  5. 胜利大逃亡(续)(状态压缩bfs)

    胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  6. C#基础:事件(一) 【转】

    前面简要介绍了委托的基本知识,包括委托的概念.匿名方法.Lambda表达式等,现在讲讲与委托相关的另一个概念:事件. 事件由委托定义,因为事件的触发方(或者说发布方)并不知道事件的订阅方会用什么样的函 ...

  7. android开发 不注意的异常

    近期刚上班,做了一个应用,要上线.測试天天測试,天天有bug,个人总结了几个常常忽略的地方: 1:在 继承 BaseAdapter 的 @Override public int getCount() ...

  8. .NET MV4 Remote远程验证注意事项及案例

    首先是模型代码 public class LoginModel { [Required] [Display(Name = "用户名")] [Remote("CheckNa ...

  9. 依赖注入及AOP简述(四)——“好莱坞原则”和依赖注入框架简介 .

    3.2.    “好莱坞原则” 看了前面关于依赖注入概念的描述,我们来提炼出依赖注入的核心思想.如果说传统的组件间耦合方式,例如new.工厂模式等,是一种由开发者主动去构建依赖对象的话,那么依赖注入模 ...

  10. Java基础笔记-String类2

    StringBuffer 特点: 是字符串缓冲区. 是一个容器,其长度可变,可以操作添加多个数据类型. 最后通过toString方法变成字符串. 被final锁修饰,因此不能被继承. 存储: 方法1: ...