C/C++函数参数列表变量的计算顺序
2015-10-15 23:02
162 查看
首先从一道2016校招阿里巴巴C++工程师的笔试题说起:
按照从右向左读取,想当然的结果应该是:11 11 10,执行后a = 12。可是真的是这样吗?写个程序验证一下吧,于是就得到了下面的结果:
很奇怪,这是为什么呢?要搞清楚什么情况恐怕得从汇编代码入手,那我们就看一下汇编代码喽:
通过查看汇编代码,我们发现在参数入栈时顺序的确是从右向左入栈,但是在入栈前先把参数列表里的表达式算一遍得到表达式的结果,最后再把这些运算结果统一入栈,这就解释了为什么第三个参数a会输出12,因为执行完a, ++a, a++后a = 12。那为什么第一项++a会输出11呢,这就要看C++中的++运算符的实现机制了,通过上面的汇编代码,可以看到:
++a对应的是: addl $0x1, 0x1c(%esp)
也就是说直接执行a+1;
a++对应的是: mov 0x1c(%esp), %eax
addl $0x1, 0x1c(%esp)
先把执行到此时的a的值备份到%eax,然后再执行+1操作,所有的表达式都执行完了,该将参数入栈了。怎么入呢?
不管是a,还是++a,入栈的代码都是:
mov 0x1c(%esp), %edx
mov %edx,0xc(&esp) | mov %edx,0x8(&esp)
也就是说直接从变量a所在的内存地址中取值。所以得到的肯定是所有运算都执行完的结果,也就是12。
而a++的入栈代码是:mov %eax,0x4(&esp)
直接把之前备份的值入栈了,而备份的时候a所在地址中保存的值是11,所以%eax中的值是11。
这样一来是不是就说通了。
总结下来有两点:
1. 在将参数入栈前,编译器会先把参数的的表达式都处理掉,哪怕这些运算会改变其中某些参数的值,
2.对于a++操作,编译器会开辟一个缓冲区来保存当前a的值,然后再对a操作,取值时是从缓冲区取,而不是直接从a的内存地址里取。
最后再验证一下理论,a = 10, printf("%d %d %d %d\n", a++, ++a, a, a++),结果应该是:12 13 13 10
参数列表中表达式的计算顺序并没有在C/C++中明确定义,每个编译器可以自由发挥,有自己的实现。
没有给定编译环境的话结果是未知的,不过至少最新版的g++与msvs2013的实现是一致的。
参考文献:博客园 Eason
Liu 鸣谢
函数B(int a, int b) { cout << a << endl; cout << b << endl; } 函数A(){ int i = 0; 函数B(++i, i++); }
按照从右向左读取,想当然的结果应该是:11 11 10,执行后a = 12。可是真的是这样吗?写个程序验证一下吧,于是就得到了下面的结果:
很奇怪,这是为什么呢?要搞清楚什么情况恐怕得从汇编代码入手,那我们就看一下汇编代码喽:
通过查看汇编代码,我们发现在参数入栈时顺序的确是从右向左入栈,但是在入栈前先把参数列表里的表达式算一遍得到表达式的结果,最后再把这些运算结果统一入栈,这就解释了为什么第三个参数a会输出12,因为执行完a, ++a, a++后a = 12。那为什么第一项++a会输出11呢,这就要看C++中的++运算符的实现机制了,通过上面的汇编代码,可以看到:
++a对应的是: addl $0x1, 0x1c(%esp)
也就是说直接执行a+1;
a++对应的是: mov 0x1c(%esp), %eax
addl $0x1, 0x1c(%esp)
先把执行到此时的a的值备份到%eax,然后再执行+1操作,所有的表达式都执行完了,该将参数入栈了。怎么入呢?
不管是a,还是++a,入栈的代码都是:
mov 0x1c(%esp), %edx
mov %edx,0xc(&esp) | mov %edx,0x8(&esp)
也就是说直接从变量a所在的内存地址中取值。所以得到的肯定是所有运算都执行完的结果,也就是12。
而a++的入栈代码是:mov %eax,0x4(&esp)
直接把之前备份的值入栈了,而备份的时候a所在地址中保存的值是11,所以%eax中的值是11。
这样一来是不是就说通了。
总结下来有两点:
1. 在将参数入栈前,编译器会先把参数的的表达式都处理掉,哪怕这些运算会改变其中某些参数的值,
2.对于a++操作,编译器会开辟一个缓冲区来保存当前a的值,然后再对a操作,取值时是从缓冲区取,而不是直接从a的内存地址里取。
最后再验证一下理论,a = 10, printf("%d %d %d %d\n", a++, ++a, a, a++),结果应该是:12 13 13 10
参数列表中表达式的计算顺序并没有在C/C++中明确定义,每个编译器可以自由发挥,有自己的实现。
没有给定编译环境的话结果是未知的,不过至少最新版的g++与msvs2013的实现是一致的。
参考文献:博客园 Eason
Liu 鸣谢
相关文章推荐
- iOS开发之C语言第一天
- C语言中微妙的bug(一)
- C++中虚函数的用法详细讲解
- c++ primer第五版(中文)习题答案 第十章第五节第二三小节-算法形参合命名规范
- C语言入门----字符
- [c++] LeetCode longest substring without repeating characters问题
- C++中文件的读写
- gen-cpp/.deps/ChildService.Plo: No such file or directory
- C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播
- c++ primer第五版(中文)习题答案 第十章第五节第一小节-5类迭代器
- C++学习笔记(第二篇)
- 非递归学习树结构(六)--RB-Tree(红黑树)
- c++primer——模板,标准库特殊设施
- C++设计模式——单例模式
- 浅析C++中的重载运算符(重点是重载+=)
- 浅析C++中的重载运算符(重点是重载+=)
- Isomorphic Strings
- 编译器定义的C/C++语言各种基本数据类型的取值范围
- C++编程有趣的标题1 于1~9填写的运算结果的中间符号等于100
- C++库一览