printf:函数参数计算从右向左,从左向右?
造冰箱的大熊猫@cnblogs 2019/8/3
1、问题
某天写了如下代码:
unsigned char ReadByteFromFile ( FILE * fp )
{
unsigned char ch;
...
fread ( &ch, , , fp );
...
return ch;
} void main()
{
...
printf ( "first byte = 0x%02x, second byte = 0x%02x\n", ReadByteFromFile ( fp ), ReadByteFromFile ( fp ) );
...
}
printf所在行的代码本意是从文件中连续读两个字节并打印出来。假设被读取文件的内容为“0x01 02 03 04 ... ...”,那么预期的运行结果是:
first byte = 0x01, second byte = 0x02
但实际运行结果(Ubuntu,gcc编译)却颠倒了个:
first byte = 0x02, second byte = 0x01
2、解答
嗯嗯,有意思。回想了很久以前上课内容并上网搜索一番,发现C标准里没有规定编译器在计算函数参数的次序(This form of argument-passing is known as call by value. The standard does not specify any order for the
evaluation of the arguments.)。也就是说,原想着printf()在运行时按照从左向右的顺序计算参数值,在这里也就顺序读取了文件中的两个字节。但实际上,编译器输出的结果却是printf()函数按照从右向左的次序计算参数,这就导致了printf()中第一个ReadByteFromFile()函数(从左向右数)后读取文件,而第二个ReadByteFromFile()却先读取文件,最终输出结果与预想的次序颠倒。
或者用Stackoverflow上某个用户提出的问题更好地说明这一问题:为什么下面代码输出结果是“4 5 5 4 5”。
main()
{
int i = 5;
printf ( "%d %d %d %d %d %d", i++, i--, ++i, --i, i);
}
因此,在使用函数中如果涉及对同一变量/对象的多次操作,一定要考虑到编译器在处理函数参数计算时次序的不确定性。建议遇到这种情况时,还是现在函数外完成计算,再将计算结果传递给printf()。当然,如果能够约定编译器中参数计算次序(最好从左向右,与日常习惯相符),还是能省些事情,让代码看起来/写起来简洁一些。
2019.8.5补充:现在回想,好像当年上课的时候有过讲授这方面的知识还有对应的考题,但真的太久远了都忘记了。
printf:函数参数计算从右向左,从左向右?的更多相关文章
- Go 初体验 - 令人惊叹的语法 - defer.3 - defer 函数参数计算时机
defer 函数的参数计算时机 定义一个 defer 函数,接收参数 n: 调用: 输出: 有点惊讶,为什么不是 100 200 200? go 语言里,defer 函数的参数是在定义位置被计算的,也 ...
- c语言中printf()函数中的参数计算顺序
今天看到了一个关于printf()函数计算顺序的问题,首先看一个例子: #include<stdio.h> int main() { printf("%d---%d---%d&q ...
- 利用可变参数模拟Printf()函数实现一个my_print()函数和调用可变参数注意的陷阱!
可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 例如,对于函数: void test(char a ...
- 可变参数模拟printf()函数实现一个my_print()函数以及调用可变参数需注意的陷阱
入栈规则 可变参数函数的实现与函数调用的栈帧结构是密切相关的.所以在我们实现可变参数之前,先得搞清楚 栈是怎样传参的. 正常情况下,C的函数参数入栈遵照__stdcall规则, 它是从右到左的,即函数 ...
- C/C++知识要点4——printf函数以及cout的计算顺序
printf函数的计算顺序:先从右到左压栈,然后从左到右出栈. 例程: #include"stdio.h" int main() { int arr[] = { 1, 2, 3, ...
- C语言 函数参数不确定时 需要用到va_start和va_end函数
1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 void foo(...);void foo(parm_list,...); 这种方式和我们以前认识的不大一样,但我 ...
- C++11的左值引用与右值引用总结
概念 在C++11中,区别表达式是左值或右值可以做这样的总结:当一个对象被用作右值的时候,用的是对象的值(内容):当对象被用作左值的时候,用的是对象的身份(在内存中的位置).左值有持久的状态,而右值要 ...
- C++11左值引用和右值引用
转载:https://www.cnblogs.com/golaxy/p/9212897.html C++11的左值引用与右值引用总结 概念 1.&与&& 对于在C++中,大家 ...
- C语言函数参数压栈顺序为何是从右到左?(从左向右的话,碰到printf的会陷入死循环)
上学期学习了汇编语言,并在操作系统实验中使用了汇编+C语言混合编程,中间也了解了一些C语言与汇编语言的对应关系. 由于汇编语言是底层的编程语言,各种函数参数都要直接控制栈进行存取,在混合编程中,要用汇 ...
随机推荐
- split(".")不生效的问题
前言:今天用String的split(".")函数分割字符串,结果总是一个空的String数组: 解决:输入的regex是一个正则表达式,很多在正则表达式里面有特殊意义的比如 &q ...
- 16.screen相关
screen -S yourname -> 新建一个叫yourname的sessionscreen -ls -> 列出当前所有的sessionscreen -r yourname -> ...
- Devexpress xaf BO中字段为RuleRequiredField必输字段时,文本标签默认添加*标记
BO中字段为RuleRequiredField必输字段时,文本标签默认添加*标记.需要在模型编辑器中设置,如图. 官网地址:https://docs.devexpress.com/eXpressApp ...
- Python 流程控制与循环体
Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...
- java lesson10homework
1. 输入三个整数x, y, z,请把这三个数由小到大输出. 2. package Homework10; 3. //:类方法 4. public class Sort { 5. voi ...
- Windows编程 Windows程序的生与死(下)
再谈程序之“死” 记得在第二回中我对程序的“死”只是一句话带过,因为我还没有铺垫好,好了现在我们可以详细的分析一下这个过程了. 这还要从while消息循环说起,还记得GetMessage函数吗?它是一 ...
- Docker 启动SQLServer
1.运行这个命令 docker run -d -e SA_PASSWORD=Docker123 -e SQLSERVER_DATABASE=qgb -e SQLSERVER_USER=sa -e ...
- Oracle 如何查看当前的实例及切换实例
一.Oracle查看当前实例 1.打开终端,输入命令sqlpuls / as sysdba连接到数据库 2.输入命令show parameter name便可以查看当前登录数据库的参数配置,如下可以看 ...
- ppp协议解析二
转:http://blog.csdn.net/yangzheng_yz/article/details/11526747 PPP(Point to Point Protocol,点对点协议)协议是为在 ...
- python爬取豆瓣电影信息数据
题外话+ 大家好啊,最近自己在做一个属于自己的博客网站(准备辞职回家养老了,明年再战)在家里 琐事也很多, 加上自己 一回到家就懒了(主要是家里冷啊! 广东十几度,老家几度,躲在被窝瑟瑟发抖,) 由于 ...