返回信息流1,2:求值顺序取决于编译器
3:先压栈max(c,d),再max(a,b)。但是max(c,d) max(a,b)的求值顺序由编译器决定。
这是一条镜像帖。来源:北邮人论坛 / cpp / #71658同步于 2013/6/8
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
Re: 有关执行顺序
amarantine
2013/6/8镜像同步17 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
函数调用还是有编译和压栈的先后顺序的。。。嵌套的函数的压栈就是标准的“扒皮”(由最内部的括号向外),如果是作为参数并列传入的话都是由后向前压栈(这里的栈指的是运行栈!),至于参数为两个函数的情况要看编译器构造的语法树是什么样子了,不过一般来说都会优先编译右侧的内容,如果是左至右顺序压栈的话,出栈编译显然会优先生成右侧的代码指令!(这里的栈指得是编译器维护的语法解析栈!)
就以你举得3为例,如给a,b,c,d赋值,a=1,b=2,c=3,d=4:
max(max(1, 2), max(3, 4));
//所生成的汇编行如下(这里分别用MinGW-gcc和VS-cl进行编译,得到的结果类似):
; 先扒皮,执行内部的两个max(...)
; 注意这里,右侧先执行,当然这里也可以先执行max(1, 2),后执行max(3, 4)
; 但max(eax, ebx)就要变为max(ebx, eax)了!这就由编译器作者的喜好而定了!
; max(3, 4);
mov [esp+1Ch+var_18], 4 ; 参数4入栈
mov [esp+1Ch+var_1C], 3 ; 参数3入栈
call sub_401398 ; 调用max,返回值在eax里
mov ebx, eax ; 返回值放入ebx暂存
; max(1, 2);
mov [esp+1Ch+var_18], 2 ; 参数2入栈
mov [esp+1Ch+var_1C], 1 ; 参数1入栈
call sub_401398 ; 调用max,返回值在eax里
; max(eax, ebx);
mov [esp+1Ch+var_18], ebx ; 这是4
mov [esp+1Ch+var_1C], eax ; 这是2
call sub_401398 ; 调用max,返回值在eax里
mov [esp+1Ch+var_1C], eax ; 结果放入栈中保存,以备后用
【 在 lcb 的大作中提到: 】
: 接着上次请教的,再请教吧。
: 3个问题:
: 1,i++!=j++;中有关i++,j++谁先执行的问题。我了解到的是,没有明确的规定。这样的写法应当避免
: ...................
来个stdcall:
push b
push a
call max
push eax
push d
push c
call max
push eax
call max
你这写得也太简略了。。。你这个如果非要把eax压进栈暂存的话是要移动栈顶指针的,第二个call就不应该和第一三个是相同的sub了!
还是mov到其它寄存器里比较方便,栈的结构没有发生变化~
【 在 FaceBasin 的大作中提到: 】
: 来个stdcall:
: push b
: push a
: ...................
stdcall不是由函数本身恢复堆栈的嘛,push eax只是为了传参而已。
【 在 tonyjansan 的大作中提到: 】
: 你这写得也太简略了。。。你这个如果非要把eax压进栈暂存的话是要移动栈顶指针的,第二个call就不应该和第一三个是相同的sub了!
: 还是mov到其它寄存器里比较方便,栈的结构没有发生变化~
:
那么,像cout<<func1()<<func2()<<endl;
运算符重载的本质其实是函数:((cout.operator<<(func1())).operator<<(func2()).operator<<(endl);
当然,这里有对象。我很纳闷:它应该肯定先执行cout.operator<<(func1())返回原来的cout对象,才能用这个cout对象接着来调用operator<<函数。结果却是先执行func2(),这怎么解释?
【 在 tonyjansan 的大作中提到: 】
: 函数调用还是有编译和压栈的先后顺序的。。。嵌套的函数的压栈就是标准的“扒皮”(由最内部的括号向外),如果是作为参数并列传入的话都是由后向前压栈(这里的栈指的是运行栈!),至于参数为两个函数的情况要看编译器构造的语法树是什么样子了,不过一般来说都会优先编译右侧的内容,如果是左至右顺序压栈的话,出栈编译显然会优先生成右侧的代码指令!(这里的栈指得是编译器维护的语法解析栈!)
: 就以你举得3为例,如给a,b,c,d赋值,a=1,b=2,c=3,d=4:
: max(max(1, 2), max(3, 4));
: ...................
继续分析:
比如func1和func2都是int型。
我们用一种叫SSA(Static Single Assignment)的表示方法:每个变量只在一个地方被赋值。通俗地说(尽管不太准确):不许覆盖以前的变量。
这个cout<<func1()<<func2()<<endl;可以翻译成以下的形式:%x表示临时变量。
%1 = call func1()
%2 = call func2()
%3 = call operator_<<(cout, %1)
%4 = call operator_<<(%3, %2)
%5 = call operator_<<(%4, endl)
可以看出,%1,%2,%3之间没有以来关系,所以编译器会认为它们的顺序可以随意调整;%4依赖于%3和%2,所以“%4=call operator_<<(%3,%2)"必须在%3和%2之后执行。同样,%5必须在%4之后执行。
所以,func1(),func2(),以及operator_<<(cout, %1)这三者的执行顺序是不定的。但是%4必须在%1,%2,%3之后执行,%5必须最后执行。
至于所谓的“压栈”,是每一次进行函数调用的时候进行的操作,也就是将操作数压到栈上。注意:压栈的顺序不等于求值的顺序。而且,即使是cout<<func1(),压入栈的是func1()计算出来以后的值,而不是计算func1()的过程。
【 在 lcb 的大作中提到: 】
: 那么,像cout<<func1()<<func2()<<endl;
: 运算符重载的本质其实是函数:((cout.operator<<(func1())).operator<<(func2()).operator<<(endl);
: 当然,这里有对象。我很纳闷:它应该肯定先执行cout.operator<<(func1())返回原来的cout对象,才能用这个cout对象接着来调用operator<<函数。结果却是先执行func2(),这怎么解释?
这里你是把它完全当成函数来看了。我觉得你没有注意cout对象。
显然如果要执行<<func2(),我们至少得清楚是哪个对象在调用其成员函数operator<<因此我们分析到,它是cout.operator<<(func1())这个函数返回的对象,既然是这个函数返回的对象(已经有返回值了),那就意味着operator<<(func1())已经执行了。那么,func1肯定得在func2之前执行呀
有关operator<<, 你说的:call operator_<<(cout, %1)应该不是这样的吧。cout难道是其中的一个参数?
【 在 nuanyangyang 的大作中提到: 】
: 继续分析:
: 比如func1和func2都是int型。
: 我们用一种叫SSA(Static Single Assignment)的表示方法:每个变量只在一个地方被赋值。通俗地说(尽管不太准确):不许覆盖以前的变量。
: ...................
【 在 lcb 的大作中提到: 】
: 这里你是把它完全当成函数来看了。我觉得你没有注意cout对象。
: 显然如果要执行<<func2(),我们至少得清楚是哪个对象在调用其成员函数operator<<因此我们分析到,它是cout.operator<<(func1())这个函数返回的对象,既然是这个函数返回的对象(已经有返回值了),那就意味着operator<<(func1())已经执行了。那么,func1肯定得在func2之前执行呀
: 有关operator<<, 你说的:call operator_<<(cout, %1)应该不是这样的吧。cout难道是其中的一个参数?
是啊,你以为“方法”是什么?只不过是个函数,隐含带一个this指针而已。