C语言执行a=a; 后,a的值应该加一还是不变?
【语言表达的简明,a语言】
结论:不同的编译器,会得出不同的结果 。因为a = a这种表达式,在C语言规范中是属于未定义的行为(Undefined behavior) 。以下面这段代码为例,在Linux上打印0x1234,在Windows上打印0x1235 。下面分别在Windows和Linux上演示,并从汇编的角度,详细讲解一下 。
Windows(Visual Studio 2015)在Windows上,用VS2015编译并运行,结果如下:看一下反汇编:蓝色方框内指令 mov dword ptr[a], 1234h 给变量a赋初值,也就是0x1234 。红色方框内两条指令,看起来挺有意思:第一条:mov eax, dword ptr[a] 把变量a的值加载到寄存器eax中 。
第二条:mov dword ptr[a], eax 又把寄存器eax的值,存放到变量a中 。这两条指令时没有任何意义的 。绿色方框内的三条指令:第一条:mov ecx, dword ptr[a] 把变量a的值加载到寄存器ecx中,也就是0x1234 。第二条:add ecx, 1 把ecx的值加1,此时ecx = 0x12341 = 0x1235 。
第三条:mov dword ptr[a], ecx 把寄存器ecx的值存放到变量a中,此时a的值是0x1235,这也是a最终的值 。不难看出,VS2015上,表达式 “a = a”被翻译成下面的伪代码表示:a = 0x1234;eax = a;a = eax;ecx = a;ecx = ecx1;a = ecx;所以,最终变量a的值是0x1235 。
Linux(GCC 8.3.0)同样的程序,在Ubuntu上用GCC 8.3.0编译,并运行,结果为0x1234 。如下图:把GCC编译生成的目标文件进行反汇编,如下图:图中,关键指令已经标记出来了,应该比较容易理解了 。不难看出,表达式“a = a”被GCC翻译成如下伪代码:a = 0x1234;eax = a;edx = eax1;a = edx;a = eax;因此,最终变量a的值是0x1234 。
sequence pointC语言中有个重要的概念 - “sequence point”,有的翻译为顺序点或序列点,还有翻译为执行点的 。C语言规范要求,编译器必须保证,在某个sequence point上,它之前的所有表达式的计算都已经确定,而它之后的所有表达式计算都尚未开始 。C语言规范中,对各种表达式定义了一系列的sequence point,感兴趣的童鞋可以翻阅C语言规范 。
对于“a = a;”这个表达式,C语言规范定义的sequence point是最后的分号“;” 。但是,这个表达式中,可能改变a的值的地方却有两个:一个是对等号左边的a赋值的操作,一个是等号右边的a的自加操作 。这两个操作之间,并没有明确定义的sequence point 。因此,才导致了不同的编译器采用不同的计算顺序,得出不同的结果 。
其实,如果编译时开启告警选项,编译时是会有告警信息的,比如GCC和Clang:建议C语言规范中,列举了很多不确定的行为,主要有:Unspecified behaviorUndefined behaviorImplementation-defined behaviorLocale-specific behavior总之,有很多语法规则,C语言规范并没有定义一个确定的输出结果,不同的编译器、版本、运行时环境、OS等都可能会产生不同的结果 。
推荐阅读
- 盐对狗狗的危害真的这么大吗,卖狗的钱为什么买盐呢
- 拉布拉多狗是什么品种,您真的了解拉布拉多吗
- 独为什么是犭傍,丝绸之路的历史地理背景
- 用什么软件拍猫咪好看,什么样的猫咪好看
- 狗为什么老爱在地上,为什么天下的狗狗都爱刨坑
- 三星pl51,我买了三星的PL51相机 价格是1270 是贵了还是便宜啊??
- 泰迪犬一般多少钱一支,受欢迎的泰迪犬
- 高端显卡笔记本,谁给推荐几款中高端显卡的笔记本4000左右的
- 升级后的手机异象
- 坚果手机系统,真是卡顿中的王者,老罗,流畅得不像...
