cout 计算顺序问题
cout输出流的执行顺序
下面是IBM的一道笔试题
#include <iostream> using namespace std;
int fun( ) {
cout << "f" ; return 1; } int main() {
int i = 1; // cout << i++ << i++ << i++ << endl; cout << "m" << fun() << fun() << fun() << endl;
return 1; }
输出fffm111
问题:cout这种连接写法的输出的执行顺序是啥呢?
cout<<"m"<<fun1()<<fun2()<<fun3()<<endl;
<<运算是左结合的。 必然先求cout<<"m"的值,值仍然是cout
然后试图求cout<<fun1()的值,这必须先求出fun1()的值。整个表达式的值仍然是cout 然后试图求cout<<fun2()的值,这必须先求出fun2()的值。整个表达式的值仍然是cout
然后试图求cout<<fun3()的值,这必须先求出fun3()的值。整个表达式的值仍然是cout
最后是cout<<endl的值,值是cout 整个表达式语句以分号结尾
注意:问题就在这里:“这必须先求出fun1()的值”,“这必须先求出fun2()的值”,“这必须先求出fun3()的值”,这3句。这是计算<<运算的前提。只要分别在计算cout<<fun1(),cout<<fun2(),cout<<fun3(),之前完成就可以了。
因此,具体是先计算fun1()的值,还是先计算fun2()的值,还是先计算fun3()的值,还是先计算cout<<"m"的值,都不影响表达式的值。
问题就在这里: 这是个<<表达式。<<本来是位运算,但是这里cout却是来利用运算的“副作用”。
所谓副作用,就是计算一个表达式的时候,除了得到它的值以外,对环境产生的影响都是副作用。 比如: int a=1,b=2,c=3,d;
d=a<<b:
这一步,a<<b计算出1左移2位得到的结果。结果是4。也就是说,赋值表达式结束后,d的值变成4,其它地方都没有改变。这就是说这个<<运算没有副作用。
但是,cout<<"a"就不一样了。这个表达式的值我们根本就不关心。我们只关心,这个表达式“计算”完以后,"a"被输出到屏幕上了。这里“a被输出到屏幕上”就是副作用。
再看这个例子: int foo(int a, int b) { return a+b; } int bar(int a, int b) { return a-b; } int a=1,b=2,c=3,d; d=foo(a,b)+bar(b,d);
这里,foo()和bar()都没有副作用。因此,这个表达式,不论是先计算foo(a,b)的值,还是先计算bar(b,c)的值,都不会影响计算的结果。
但是,如果是这个例子: int foo(int* a) { (*a)++; return *a;} int bar(int* a) { (*a)--; return *a;} int a=5,b; b=foo(&a)+bar(&a);
这个表达式,foo()和bar()都有副作用,所以,先计算foo(&a)还是先计算bar(&a),将直接影响到b的值。
假如先计算foo,再计算bar。 首先,a=5 计算foo(&a),a变成6,foo(&a)的值是6
计算bar(&a),a变成5,bar(&a)的值是5 这样,b=6+5=11 假如先计算bar,再计算foo。
首先,a=5 计算bar(&a),a变成4,foo(&a)的值是4
计算foo(&a),a变成5,bar(&a)的值是5 这样,b=5+4=9 这就造成了计算结果不一致。 === 那。。。怎么办 一般来说,编c/c++程序有一个纪律:一个语句中不要有两个表达式有副作用。
典型的这类行为包括:b=(a++)+(a++)+(a++);
这是典型的违反这条纪律的行为。每个a++都有副作用(改变a的值)。整个表达式的值跟求值顺序直接相连。 还有就是 char* fun() { cout<<"q"; return ""; }
cout<<"m"<<fun()<<fun()<<fun()<<endl;
每个fun()都有副作用(向屏幕上显示字符)。因此效果直接与求值顺序相关。(而整个表达式的值我们根本就不关心。虽然我知道,值就是cout)。
======
在c/c++中,求值顺序是怎么样的?
不知道。
C/C++的规范中,求值顺序是不规定的。这是为了给编译器以优化的空间。
比如:
b=(a+2)+(a+2);,那么如果只计算一次a+2的值,而不是两次,那么计算量会大大降低。
因此,
不要在C语言里面做这种事情:
char* fun() { cout<<"q"; return ""; }
cout<<"m"<<fun()<<fun()<<fun()<<endl;
要这样:
char* fun() { return "q"; }
cout<<"m"<<fun()<<fun()<<fun()<<endl; // 输出的一定是 "mqqq\n"
这样更好:
string fun() { return string("q"); }
cout<<"m"<<fun()<<fun()<<fun()<<endl; // 输出的一定是 "mqqq\n"
这样就更好了:
string f="q"; // 隐式转换
cout<<"m"<<fun()<<fun()<<fun()<<endl; // 输出的一定是 "mqqq\n"
但是这样不好吗?: cout<<"mqqq"<<endl;
这应该只是个测验。我相信IBM的软件工程师们不会编出这种垃圾代码的。
代码稍微修改了下
#include <iostream>
using namespace std;
int fun(int i) { cout << "f"<<i; return i; }
int main() {
int i = 1; cout << i++ << i++ << i++ << endl; cout << "m" << fun(1) << fun(2) << fun(3) << endl; cin.get(); return 1; }
输出结果: 321 f3f2f1m123
如果只针对题来说的话,实际是这样的
cout<<"m"<<fun()<<fun()<<fun();
对于<<其实是从右往左处理的。于是碰到fun()必然先输出f,然后返回1,于是就变成了
cout<<"m"<<fun()<<fun()<<1; 继续往左走,直到 cout<<"m"<<1<<1<<1 ;的时候已经输出了fff ,之后就是按顺序输出了m111, 所以看到的结果就是 fffm111
cout 计算顺序问题的更多相关文章
- C/C++中printf/cout 计算顺序与缓冲区问题
1.printf/cout在同一个语句中都是从右向左计算的. 看如下的代码: #include <stdio.h> int main() { ; printf("%d %d&qu ...
- C/C++知识要点4——printf函数以及cout的计算顺序
printf函数的计算顺序:先从右到左压栈,然后从左到右出栈. 例程: #include"stdio.h" int main() { int arr[] = { 1, 2, 3, ...
- 有关C/C++中,表达式计算顺序的问题,以及表达式内部变量“副作用”问题(转)
经常可以在一些讨论组里看到下面的提问:“谁知道下面C语句给n赋什么值?”m = 1; n = m+++m++;最近有位不相识的朋友发email给我,问为什么在某个C++系统里,下面表达式打印出两个4, ...
- c++cout执行顺序之一个不容易注意到的一点
二话不说,先看一个例子 #include <iostream> using namespace std; int main() { ]={,,,,,,,,,}; int *p=a; int ...
- C++ cout执行顺序
C++ cout执行顺序 问题描述是这样的:如果在cout中调用函数,同时这个函数中包含输出语句,那么会先输出哪一句? 仔细一看,突然发现对C++的内容遗忘了,确实一下子看不出来输出的先后问题. 实现 ...
- c语言中printf()函数中的参数计算顺序
今天看到了一个关于printf()函数计算顺序的问题,首先看一个例子: #include<stdio.h> int main() { printf("%d---%d---%d&q ...
- printf函数对参数的计算顺序
没想到啊,没想到: printf函数对参数的计算顺序是从右往左的! 我不禁想问一句,这么坑爹的事情,书里居然没有写过.还是我看书不仔细,没有找到?(回头,在自己翻翻那本c语言编程) 于是下面的程序结果 ...
- Python C3 算法 手动计算顺序
Python C3 算法 手动计算顺序 手动计算类继承C3算法原则: 以所求类的直接子类的数目分成相应部分 按照从左往右的顺序依次写出继承关系 继承关系第一个第一位,在所有后面关系都是第一个出现的 ...
- C/C++多参数函数参数的计算顺序与压栈顺序
一.前言 今天在看Thinking in C++这本书时,书中的一个例子引起了我的注意,具体是使用了下面这句 单看这条语句的语义会发现仅仅是使用一个简单的string的substr函数将所得子串pus ...
随机推荐
- JavaWeb基础:Servlet
Servlet 基本概念 Servlet是Sun公司提出的一种用于开发动态Web资源的技术规范: Servlet是一个Java接口, 用户想要开发自定义的Servlet可以通过以下步骤: 编写实现Se ...
- UML类图关系大全
UML类图关系大全 1.关联 双向关联: C1-C2:指双方都知道对方的存在,都可以调用对方的公共属性和方法.在GOF的设计模式书上是这样描述的:虽然在分析阶段这种关系是适用的,但我们觉得它对于描述设 ...
- 读《程序员的SQL金典》[1]--基础数据检索
前言 <程序员的SQL金典>这本书是杨中科老师的,拜读了一下,简单做了读书笔记供以后翻阅.仅供学习分享,要想细读的话推荐购买原版呀! 这次读书的时候用了新的办法把看书计划进行了量化,虽然简 ...
- Java开发 Eclipse使用技巧(转)
1.如何设置默认的代码目录为src,默认的输出目录为bin? window->Preferences->java->Build Path中,右侧选择Folders就可以 2.如何为快 ...
- 第七章 企业项目开发--本地缓存guava cache
1.在实际项目开发中,会使用到很多缓存技术,而且数据库的设计一般也会依赖于有缓存的情况下设计. 常用的缓存分两种:本地缓存和分布式缓存. 常用的本地缓存是guava cache,本章主要介绍guava ...
- 137. Single Number II——问题是查找,本质是hash查找,只是记录的是32 bit中各个位出现次数而已
Given an array of integers, every element appears three times except for one. Find that single one. ...
- 学习记录014-ssh批量分发
一.ssh服务介绍 1.ssh安全的加密协议用于远程连接服务器 2.默认端口是22,安全协议版本ssh2,它能同时支持RSA和DSA秘钥,SSH1只支持RSA 3.服务端主要包含两个服务功能ssh远程 ...
- node.js+WebStorm路径问题
目录路径 :A文件夹下有B.C文件夹和app.js文件.B文件夹下有webserver.js文件等. A B webserver.js ...... C ...... app.js WebStorm配 ...
- 第三方开源水面波浪波形view:WaveView
一个比较有趣的Android第三方开源波形view:WaveView,这种WaveView在一些常见的APP开发中,以水面波浪波形的形象的生动展示手机还剩余多少电量,存储容量还有多少等,比较形象直观生 ...
- 动态加载的数据,hover效果
$('.option li').live('mouseenter',function(){ $(this).addClass("active"); ...