i++和++i作为参数时的编译器处理方式分析~

【补充声明】此文完成于几年前回答 BCCN 论坛的网友提问,就问题本身而言,对于这个问题似乎是没必要深究的,因为这种代码在读取一个变量的值的过程中反复尝试修改它的值,其结果依赖编辑器的实现。这种代码当然也是不可能在现实应用中出现的。不过作为一个问题,如果他一定要问,某编译器为什么会给出这样的结果,那就必须了解编译器对这个代码的编译结果细节,这就是本文所论述的东西。本文只涉及到了 TC2, VC6, VC2005 几种编译器。而且后两者应该使用的 WIN32 DEBUG,通常 Release 版本和 Debug 版本是一种在运行结果表现上的等效关系,对这个具体问题在当时我并未有精力再去细分。此次更新编辑顺便修改了原文中的个别错别字。原文中的术语“堆栈”修改为“栈”。原文中的一些中间结论不够准确,但暂未删除,已经用删除线做了处理。

    ---- hoodlum1980 ,2012年9月10日。



 

首先我们来看一下这个问题的提出,来自于一个网友的提问:
http://bbs.bccn.net/thread-200774-1-1.html
----------------------------------------------------------------------------------------------------------

求教大家,简单问题,但为什么是这样的结果?(vc6.0)

很简单的程序
void main()
{
     int i=8;
     printf("%d,%d,%d,%d\n", ++i, --i, i++, i--);
}
但是结果为(8 7 8 8)无论是从左到右顺序求值还是从右到左顺序求值都不应该是这个结果吧?
我觉得从左到右应该是(9 8 8 9 )从右到左是(8 7 7 8),
是我的错还是编译器的原因?如果是从右到左顺序求值,为什么结果不是(8 7 7 8)而是(8 7 8 8)
请大家指点一下!
[ 本帖最后由 默默无纹 于 2008-2-24 21:04 编辑 ]
-----------------------------------------------------------------------------------------------------------
    在这里我使用了VS.NET2005编译的结果是:8,8,7,8。用TC2.0编译的结果是:8,7,7,8。VC6.0我没有安装,所以没有试过,也没办法分析。
    这里我们可以看到,由不同的编译器产生了不同结果,可见这个问题是依赖编译器的理解和实现的。换句话说,对于 i++ 和 ++i 的处理在这里是有歧义的,当然在自己应用中我相信也不会有任何人写出这样的代码。但是作为一个问题,我们有必要分析一下不同编译器究竟如何理解i++和++i操作符的。

        我们在学习C的时候,应该已经大概知道了 i++ 和 ++i 两者的区别,即“++”符号在 i 之前还是之后,决定了 i 自增操作和他的语句的执行顺序的关系。即i++,理解为i在其语句中取原始值,++i在其语句中取自增后的新值。这一点是毫无疑义的。但是问题在于,网友的问题中又涉及到了 i++,++i 在作为参数时候的处理,所以这时候我们就会感到困惑,i++ 和 ++i 在作为参数的时候,和进入栈的顺序之间有何关系呢?根据前面的实验,可见TC2.0和VS.net2005的处理不同,可见两者对其处理不同,那么造成这种不同的结果的原因是什么呢?我们从代码上无法看到差异,因此我们必须看汇编语言才能知晓,编译器到底把我们的代码翻译成了什么样子。下面我采用IDA反汇编编译器把生成的.exe文件,结果如下:

 

VS.NET 2005的代码
                                     mov     [ebp+var_8], 8      //i=8
.text:004113E5                 mov     eax, [ebp+var_8]    //  
.text:004113E8                 mov     [ebp+var_D0], eax   //i--之前把i的值保存到,temp[0]=8   
.text:004113EE                 mov     ecx, [ebp+var_8]     
.text:004113F1                 sub     ecx, 1               //i--,(从右向左数第一个参数)
.text:004113F4                 mov     [ebp+var_8], ecx     //i=7
.text:004113F7                 mov     edx, [ebp+var_8]     
.text:004113FA                 mov     [ebp+var_D4], edx   //i++之前把i保存到:temp[1]=7 (这时候已经执行过i--了)
.text:00411400                 mov     eax, [ebp+var_8]    //i++,(从右向左数第二个参数) 
.text:00411403                 add     eax, 1
.text:00411406                 mov     [ebp+var_8], eax    //i=8
.text:00411409                 mov     ecx, [ebp+var_8]    
.text:0041140C                 sub     ecx, 1               //--i, 无需保存修改前的值,直接改变实参i
.text:0041140F                 mov     [ebp+var_8], ecx    //i=7
.text:00411412                 mov     edx, [ebp+var_8]
.text:00411415                 add     edx, 1              //++i,
.text:00411418                 mov     [ebp+var_8], edx    //i=8
.text:0041141B                 mov     esi, esp
.text:0041141D                 mov     eax, [ebp+var_D0]   //压栈temp[0]=8
.text:00411423                 push    eax
.text:00411424                 mov     ecx, [ebp+var_D4]   //压栈temp[1]=7
.text:0041142A                 push    ecx
.text:0041142B                 mov     edx, [ebp+var_8]    //压栈i=8
.text:0041142E                 push    edx
.text:0041142F                 mov     eax, [ebp+var_8]    //压栈i=8
.text:00411432                 push    eax
.text:00411433                 push    offset aDDDD    ; "%d,%d,%d,%d\n"  //压栈字符串"%d,%d,%d,%d\n"的地址   
.text:00411438                 call    ds:printf            //调用打印函数,输出8,8,7,8
.text:0041143E                 add     esp, 14h
.text:00411441                 cmp     esi, esp
.text:00411443                 call    sub_41113B
.text:00411448                 mov     esi, esp

    可见,++i和--i执行的时候直接改变了i的值,而i++和i--必须在所在的这个语句执行后才能改变i的值,所以i++作为参数时,实际上是这样的过程,
         printf("%d",i++);

    相当于下面的语句:
         int temp=i;
           i = (i+1);
         printf("%d",temp);

 

    因此上面的代码可以翻译为:
        int i=8;
          printf("%d,%d,%d,%d",++i,--i,i++,i--);

 

    因此可以翻译为下面的等效代码:
    i=8;
    temp0=i; //temp0=8;
    i--;     //7
    temp1=i; //temp1=7
    i++;     //8
    --i;     //7
    ++i;     //i=8
    printf("%d,%d,%d,%d",i,i,temp1,temp0);   


    所以打印结果是8,8,7,8

---------------------------------------------------------------------------------------------------------
    我们再看在TC2.0下的反汇编代码:

TC2.0下面的反汇编代码
----------------------TC2.0反汇编结果--------------------
0:01FA sub_1FA         proc near               ; CODE XREF: start+11A

时间: 2024-09-23 11:38:20

i++和++i作为参数时的编译器处理方式分析~的相关文章

c++-比较两个相等的函数里,当给两个不相等参数时,系统为什么会自动返回x的值呢??

问题描述 比较两个相等的函数里,当给两个不相等参数时,系统为什么会自动返回x的值呢?? int fun(int aint b){ if(a == b) { return a; }} void main(){ int x = 20y =10; int z = fun(xy); cout<<z<<endl;}fun函数里,没有给出,当两个参数不相等的返回情况,那么系统为什么会自动返回x的值呢??请各位大神帮帮我 解决方案 由于不相等时没有给定返回值,所以将返回栈顶值如果此时栈是空的,则

[Remoting FAQ]传递Remoting参数时遇到的两种常见错误

[Remoting FAQ] 传递Remoting参数时遇到的两种常见错误 Version Date Creator Description 1.0.0.1 2006-4-25 郑昀@Ultrapower 草稿   继续阅读之前,我们假设您熟悉以下知识: n         Remoting [现象1] 我们先来描述一个简单的错误.当你激活远端Remoting Objects时,却得到了这样的错误提示: 提示信息 Type 'Common.BTRequest' in Assembly 'Comm

c#-C#调用C DLL(char *传出参数)时,报错参数不匹配

问题描述 C#调用C DLL(char *传出参数)时,报错参数不匹配 public class LLht { //手持机下载上传 [DllImport(""llht.dll"" CallingConvention = CallingConvention.StdCall CharSet = CharSet.Ansi SetLastError = true ExactSpelling = true)] public static extern int llht_dow

redirect-spring mvc 重定向,手动拼接参数时,参数中带有中文为什么会出现丢字的现象?

问题描述 spring mvc 重定向,手动拼接参数时,参数中带有中文为什么会出现丢字的现象? spring mvc 重定向,手动拼接参数时,参数中带有中文为什么会出现丢字的现象?如:redirect:/search.htm?query=蘑菇音乐节,跳转的时候发现蘑菇音这三个字丢了,求大神

错误-linux的shell为何在输入参数时不对路呀

问题描述 linux的shell为何在输入参数时不对路呀 为何这个脚本输出的不是我$1 输入的文件,发现用cat $1也不行? 解决方案 sh yourfile.sh 文件绝对路径 eg: sh yourfile.sh /etc/ 解决方案二: 因为你的$1放在了引号中,所以当成普通字符串了. 解决方案三: echo 输出的是你$1文件的文件内容

数据-求教啊?用织梦dedecms修改后台系统基本参数时出现页面布局乱了

问题描述 求教啊?用织梦dedecms修改后台系统基本参数时出现页面布局乱了 哪位高手能给指点一下吗?本人新手最近用dedecms在已建好的模板上修改,但是修改系统基本参数或者对应的数据库表中数据时网页布局就会乱掉,而且{dede:}标签在调用数据库表中数据时有的不能正常调用,为什么啊?是原作者做了什么权限吗? 解决方案 这个看你修改的基本参数是哪一些基本参数了,乱改肯定会出问题

c# 调用vc++写的动态库 时含有指针参数时的问题

问题描述 c#调用vc++写的动态库时含有指针参数时的问题[DllImport("yhybReckoning.dll",EntryPoint="_DataDown_sp@12",CharSet=CharSet.Unicode,CallingConvention=CallingConvention.Cdecl)]publicstaticunsafeexternstringDataDown_sp(stringDownLoadStr,stringDownloadLSH,r

ios block作为参数时,自身的参数是谁传递的

问题描述 ios block作为参数时,自身的参数是谁传递的 NSArray *array = [NSArray arrayWithObjects: @"A", @"B", @"C", @"A", @"B", @"Z",@"G", @"are", @"Q", nil]; NSSet *filterSet = [NSSet set

javaweb-jsp页面通过href向servlet类页面传递中文参数时出现乱码了怎么办?

问题描述 jsp页面通过href向servlet类页面传递中文参数时出现乱码了怎么办? jsp页面通过href向servlet类页面传递中文参数时出现乱码了怎么办?我在doGet方法中设置了response.setContentType("text/html"); response.setCharacterEncoding("utf-8"); request.setCharacterEncoding("utf-8");所有的编码方式都是utf-8