首页 > 手机 > 配件 > 语言表达的简明,a语言

语言表达的简明,a语言

来源:整理 时间:2022-04-07 19:29:27 编辑:华为40 手机版

C语言执行a=a ; 后,a的值应该加一还是不变?

C语言执行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 = 0x1234 1 = 0x1235。

第三条:mov dword ptr[a], ecx 把寄存器ecx的值存放到变量a中,此时a的值是0x1235,这也是a最终的值。不难看出,VS2015上,表达式 “a = a ”被翻译成下面的伪代码表示:a = 0x1234;eax = a;a = eax;ecx = a;ecx = ecx 1;a = ecx;所以,最终变量a的值是0x1235。

Linux(GCC 8.3.0)同样的程序,在Ubuntu上用GCC 8.3.0编译,并运行,结果为0x1234。如下图:把GCC编译生成的目标文件进行反汇编,如下图:图中,关键指令已经标记出来了,应该比较容易理解了。不难看出,表达式“a = a ”被GCC翻译成如下伪代码:a = 0x1234;eax = a;edx = eax 1;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等都可能会产生不同的结果。

文章TAG:语言表达简明语言

最近更新