t和printf的缓冲机制

2023-05-16

众所周知,cout和buffer都是有缓冲的(网上很多把cout和printf混用出错归结为一个有缓冲,一个无缓冲,事实会在下面说明)

cout和printf的输出是先从右往左读入缓冲区,再从top到bottem输出

对,这里的缓冲区相当于堆 栈的效果

a = 1; b = 2; c = 3;

cout<<a<<b<<c<<endl;

buffer:|3|2|1|<-   (take “<-” as a poniter)

output:|3|2|<-     (output 1)        |3|<-       (output 2)        |<-         (output 3)
然后我试了试下面的code:

#include <iostream> 
using namespace std; 
int c = 6;
int f() 
{       
    c+=1;     
    return c; 

int main() 

     int i = 0; 
     cout <<"i="<<i<<" i++="<<i++<<" i--="<<i--<<endl;
     i = 0;     
     printf("i=%d i++=%d i--=%d\n" , i , i++ ,i-- ); 
     cout<<f()<<" "<<f()<<" "<<f()<<endl; 
     c = 6;    
     printf("%d %d %d\n" , f() , f() ,f() );   
     system("pause");    
     return 0;
}
Under VS2005, the out put is

i=0 i++=-1 i--=0i=0 i++=-1 i--=09 8 79 8 7
But under g++( (GCC) 3.4.2 (mingw-special)), the out put is,

i=0 i++=0 i--=1i=0 i++=-1 i--=09 8 79 8 7
g++的输出有点出乎我的意料之外。原以为这是一个 bug in g++,于是去so上问了下
结果上面的牛人跟我讲,根本不是bug的问题: 
The output of:

printf("i=%d i++=%d i--=%d\n" , i , i++ ,i-- );
is unspecified. This is a common pitfall of C++: argument evaluation order is unspecified.

原来在C/C++的函数里,参数的调用不是按顺序来的,这是一个陷阱。所以在不同的编译器,不同的编译时间,都有可能结果不同。

下面还有人补充:
It's worse than unspecified; it's undefined. There are no guarantees that the result will match any order of evaluation, although that's usually what happens given the obvious implementation.

但是为什么cout的输出每次都一样呢?这是偶然的吗?
当然不是了,上面的人告诉我:

Not so with the cout case: it uses chained calls (sequence points), not arguments to a single function, so evaluation order is well defined from left to right.

===========================================================================
看到这里,如果我告诉你上面关于那个“cout和printf的输出是先从右往左读入缓冲区,再从top到bottem输出”完全是瞎扯的话,你的第一反应是什么?不会吧,我的机器上也是这样的结果之类的云云?其实这是一个参数调用顺序的问题,而很不幸的是,C/C++里,关于参数调用的顺序是一个 undefined behavior,也就是说,不管什么顺序的调用都是合理的,这依赖于compiler的实现。当然,如果参数的传递是用stack来实现的话,很有可能就是上面的结果。

SO上的人告诉我:

1.
You are mixing a lot of things. To date:

Implementation details of cout 
Chained calls 
Calling conventions 
Try to read up on them separately. And don't think about all of them in one go.

printf("i=%d i++=%d i--=%d\n" , i , i++ ,i-- );

The above line invokes undefined behavior. Read the FAQ 3.2. Note, what you observe is a side-effect of the function's calling convention and the way parameters are passed in the stack by a particular implementation (i.e. yours). This is not guaranteed to be the same if you were working on other machines.

I think you are confusing the order of function calls with buffering. When you have a cout statement followed by multiple insertions << you are actually invoking multiple function calls, one after the other. So, if you were to write:

cout << 42 << 0;
It really means: You call,

cout = operator<<(cout, 42)
and then use the return in another call to the same operator as:

cout = operator<<(cout, 0)
What you have tested by the above will not tell you anything cout's internal representation. I suggest you take a look at the header files to know more.


2.

Just as a general tip, never ever use i++ in the same line as another usage of i or i--.

The issue is that function arguments can be evaluated in any order, so if your function arguments have any side-effects (such as the increment and decrement operations) you can't guarantee that they will operate in the order you expect. This is something to avoid.

The same goes for this case, which is similar to the actual expansion of your cout usage:

function1 ( function2 ( foo ), bar );

The compiler is free to evaulate bar before calling function2, or vice versa. You can guarantee that function2 will return before function1 is called, for example, but not that their arguments are evaluated in a specific order.

This becomes a problem when you do something like:

function1 ( function2 ( i++), i );

You have no way to specify whether the "i" is evaluated before or after the "i++", so you're likely to get results that are different than you expect, or different results with different compilers or even different versions of the same compiler.

Bottom line, avoid statements with side-effects. Only use them if they're the only statement on the line or if you know you're only modifying the same variable once. (A "line" means a single statement plus semicolon.)

3.

n addition to the other answers which correctly point out that you are seeing undefined behavior, I figured I'd mention that std::cout uses an object of type std::streambuf to do its internal buffering. Basically it is an abstract class which represents of buffer (the size is particular to implementation and can even be 0 for unbufferd stream buffers). The one for std::cout is written such that when it "overflows" it is flushed into stdout.

In fact, you can change the std::streambuf associated with std::cout (or any stream for that matter). This often useful if you want to do something clever like make all std::cout calls end in a log file or something.

And as dirkgently said you are confusing calling convention with other details, they are entirely unrelated to std::cout's buffering.

对,那个输出顺序啥也不能说明,仅仅是一个参数调用的顺序而已,跟buffer一点关系都没有。

关于implementation-defined unspecified和undefined这3种行为,可以参考这个

TopLanguage上也有类似的回答:

> i = 0; 
> printf("i=%d i++=%d i--=%d\n" , i , i++ ,i-- );函数参数计算顺序(不是压栈顺序)是依赖编译器的,平时这么写不是好习惯。

也有人跟我讲cout<<a<<b<<c 的调用原型其实是 operator<< ( operator<< ( operator<<


对于操作符,要看是否使用了重载。 
对于c和未重载的c++操作符号,如果不是序列点运算符,其操作数的计算顺序也是编译器依赖的。如 
int i=1; 
(i++)<<(i++)<<(i++); 
(++i)-(++i)-(++i); 
两个表达式给出任何结果都是合理的,因为依赖编译器。

如果是c++重载了的操作符,它就不是“操作符”,而是函数了, 但是其优先级别和计算顺序是保留的。所以, 
ostream i; 
i<<a<<b<<c; 
等价于 (op<<(i, a))<<b<<c 
等价于 op<<((op<<(i, a), b)<<c 
等价于 op<<(op<<((op<<(i, a), b), c)

但是顺然形式相等,可是a,b,c的计算顺序仍然是编译器依赖的。 
因为op<<((op<<(i, a), b)可能现于c计算,(op<<(i, a))可能先于 b 计算,但怎样的结果都是合理的。


===========================================================================
然后这里说说为什么最好cout不要和printf混用的问题

下面来做一些试验(环境:g++ (GCC) 3.2.3 (mingw special 20030504-1))。#include <iostream>
using namespace std;int main() {
    cout << "aaa";
    printf("bbb");
    return 0;
}输出为:aaabbb没有问题。如果将程序修改一下:#include <iostream>
using namespace std;int main() {
    ios::sync_with_stdio(false);
    cout << "aaa";
    printf("bbb");
    return 0;
}输出成了:bbbaaa顺序发生了错误。sync_with_stdio()是在<ios_base>中定义的,当其接受true作为参数时,将会同步iostream与 stdio中的流操作。默认是true,因此第一个程序的结果是正确的。然而,尽管C++标准中规定stdio sync标志默认是true,不同平台下的不同编译器可能并不完全支持这个标准。因此也就有了通常意义上的关于“不要混用iostream与stdio” 之类的警告。如果再修改一下程序:#include <iostream>
using namespace std;int main() {
    ios::sync_with_stdio(false);
    cout << "aaa" << flush;
    printf("bbb");
    return 0;
}

这回程序的输出就又正确了。因为flush强制清空了缓冲区,将其中的内容输出。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

t和printf的缓冲机制 的相关文章

  • 串口接收无定长数据

    1 原理 xff1a 1 使能串口接收中断 定时器中断 xff1b 2 在串口第一次进入到中断后 xff0c 使能定时器计时 xff1b 3 在串口每次进入中断后 xff0c 清空定时器 xff1b 4 当定时器溢出时 xff0c 判定数据
  • OpenWRT 小记

    查看openwrt内核版本 xff1a cat proc version uname r 生成配置文件 xff1a config generate 查看DHCH 已经分配的IP cat var dhcp leases 分割cat tmp d
  • OpenWrt OpenMPTCProuter feed

    echo 34 src git OpenMPTCProuter https github com Ysurac openmptcprouter feeds git 34 gt gt feeds conf default echo 34 sr
  • openwrt路由器接华为E3372(E8372)网卡实现4G转有线和WIFI

    Hilink 在openwrt系统中安装kmod usb net rndis kmod usb net kmod usb2 usb modeswitch kmod usb net cdc ether 安装完成后 xff0c 把E3372 x
  • windows 10 内存居高不下,实际没开多少进程

    windows 10 内存居高不下 xff0c 实际没开多少进程 关闭快速启动 就好了
  • opkg list 报错

    opkg list Collected errors opkg conf load Could not lock var lock opkg lock Resource temporarily unavail echo 34 nameser
  • openwrt opkg install 强制替换安装

    查询 opkg list installed grep XXX opkg install XXX ipk force downgrade
  • stm32 硬件spi半双工三线的一些研究心得

    a7105可以使用四线spi 或者3线spi 但是之前都是使用3线的软件模拟的三线spi的 xff0c 所以不想改其它代码了 xff0c 就想可以提高一个spi的读写速度 xff0c 原来软件方式的读写速度 xff0c 在48Mhz的03x
  • Openwrt tftp刷机

    第一次写论坛 xff0c 今天早上才拿到路由器 开始学习openwrt 之前学过嵌入式Linux arm 移植 xff0c 开始正题 xff1a 拿到开发板后 xff0c 就开始烧写自己编译的 bin文件 xff0c 在烧写的过程中出现了问
  • openwrt ipk 安装 luci 界面

    试试看可行不 慢慢更新 opkg update 更新 opkg list grep svn
  • OpenWRT php 安装

    一 安装PHP opkg update opkg install php5 php5 mod apc opkg install php5 mod gd php5 mod session opkg install php5 mod pdo m
  • ESP8266 固件擦除

    折腾了两天 真是醉了 首先确认安装 python python2是否安装 python2 version sudo apt isntall python pip 安装pip和他的许多其他依赖 pip 9 0 1 from usr lib p
  • 第二次实验报告:使用Packet Tracer分析应用层协议

    姓名 xff1a 刘钰学号 xff1a 201821121036班级 xff1a 计算1812 1 实验目的 熟练使用Packet Tracer工具 分析抓到的应用层协议数据包 xff0c 深入理解应用层协议 xff0c 包括语法 语义 时
  • C++的类与C语言结构体比较

    C 43 43 的类与C语言结构体比较 C 43 43 的类与C语言结构体比较 一 结构体 xff0c 类的介绍二 结构体和类的具体区别1 C语言对结构体数组初始化 必须要在定义时初始化 xff1a 2 C 43 43 的类的初始化 构造函
  • CPP-网络/通信:经典HTTP协议详解

    2008 11 03 09 11 by Hundre 266688 阅读 23 评论 收藏 编辑 转自 xff1a http blog csdn net gueter archive 2007 03 08 1524447 aspx Auth
  • 串口编程3:使用串口读取GPS信息

    关于GPS的使用 xff0c 参考 本文主要参考的博客 xff0c 在此表示感谢 xff01 xff01 xff01 主函数 主函数gps main c xff0c 这里便涉及到了串口的打开 xff0c 读操作 xff0c 以及调用了串口设
  • 基于单片机语音智能导盲仪仿真设计-毕设课设资料

    资料下载地址 1110 xff08 百度网盘 xff09 xff1a 点击下载 包含超声波传感器检测障碍物 xff0c 温度传感器检测当前温度 可以通过按键设置距离报警范围 xff0c 报警装置通过声光报警 包含的电路有电源电路 显示电路
  • 宏定义详解

    宏定义有无参数宏定义和带参数宏定义两种 无参数的宏定义的一般形式为 define 标识符 字符序列 其中 define之后的标识符称为宏定义名 简称宏名 xff0c 要求宏名与字符序列之间用空格符分隔 这种宏定义要求编译预处理程序将源程序中
  • 一个无线鼠标的HID Report Desc

    HID设备是USB规范定义的设备类型之一 xff0c 其分类号为0x03 关于USB设备类型定义 xff0c 可参见本站 xff1a USB设备类型定义 USB中文网 HID设备除了用于专门的输入输出设备外 xff0c 有时也与其它的设备类
  • 虚拟机的三种网络连接方式

    1 NAT模式 xff1a 用于共享主机的IP地址 安装完VMware后在本地网络连接里会虚拟出两块网卡 xff08 VMnet1 xff0c VMnet8 xff09 如果选择的是NAT模式 xff0c 则会使用VMnet8这块网卡来和虚

随机推荐

  • 全局变量不能放在头文件当中

    看网上各种说法说 变量的声明和变量的定义 xff0c 但是还是没有讲清楚什么是声明什么是定义 xff0c 如果说定义要分配内存 xff0c 声明不分配 xff0c 这个谁都知道 刚我在VS2012中测试 xff1b 按理说 int i xf
  • 使用strcat连接字符串

    include lt iostream gt using namespace std int main int argc char argv char str1 61 34 hello 34 char str2 61 34 china 34
  • 单片机学习笔记————51单片机实现常用的自定义串口通讯协议

    proteus虚拟串口的实现 xff1a https mp csdn net console editor html 107251649 一 使用proteus绘制简单的电路图 xff0c 用于后续仿真 二 编写程序 64 Project
  • Uart串口收发回环验证

    Uart串口收发回环验证 接受模块发送模块波特率设置模块顶层模块TBModelsim仿真结果板级验证总结 本次所做的项目比较复杂 xff08 对我本人来讲 xff09 xff0c 设计一个Uart IP核 xff0c 在其基础 xff0c
  • C++ vector的用法(整理)

    vector 是向量类型 xff0c 它可以容纳许多类型的数据 xff0c 如若干个整数 xff0c 所以称其为容器 vector 是C 43 43 STL的一个重要成员 xff0c 使用它时需要包含头文件 xff1a include lt
  • 示波器的使用

    示波器的使用 在家电维修的过程中使用示波器已十分普遍 通过示波器可以直观地观察被测电路的波形 xff0c 包括形状 幅度 频率 xff08 周期 xff09 相位 xff0c 还可以对两个波形进行比较 xff0c 从而迅速 准确地找到故障原
  • 谈谈嵌入式系统的可靠性和安全性

    这里谈的安全性 xff0c 跟通用计算机所说的网络安全性不是一个概念 xff0c 网络安全性指的是数据不被人为破坏和窃取 xff0c 计算机不被恶意控制 而这里谈的安全性 xff0c 指的是设备安全 xff0c 例如自动化生产线不夹断工人手
  • 裸奔和rtos下开发的差异分析

    嵌入式设备网络化 u盘化 功能复杂化的趋势 xff0c 使越来越多的 过去可以用裸奔实现的嵌入式产品 xff0c 产生了应用操作系统的需求 而人力成本的持续上升 芯片成本的连续下降 xff0c 以及cpu性能的迅速提高 xff0c 又为大面
  • “全员编程,广泛嵌入”(六)—— 物联网时代(四)

    物联网操作系统是个伪命题 xff0c 很多人炒物联网概念 xff0c 炒物联网操作系统 xff0c 其实 xff0c 物联网操作系统 xff0c 与其说是一个技术概念 xff0c 还不如说是一个商业概念 这也无可厚非 xff0c 你不炒 x
  • 高通Qualcomm处理器的手机或设备进EDL 9008模式的办法

    适用于变砖的设备 由于我们有很多基于 Qualcomm 的设备 xff0c 其中一些设备可能会古怪地猜测如何进入 EDL 模式 xff0c 或者如何正确进入 例如 xff0c 对于 Alcatel xff0c 您必须先按住两个音量键 xff
  • 远程桌面能解决物联网和智能硬件的什么问题

    前几篇 xff0c 讲了许多远程桌面的功能和应用 xff0c 但还是有不少网友不明白 xff0c 在QQ上跟我讨论 xff0c 在物联网和智能硬件方面 xff0c 究竟能解决什么问题 智能硬件发展这么多年 xff0c 总是雷声大雨点小 xf
  • 物联网技术上面临的基本问题和操作系统设计

    时下 xff0c 在操作系统界 xff0c 有一个热得发紫的名词 物联网操作系统 xff0c 但物联网和操作系统究竟是什么关系 xff0c 物联网将面临什么问题 xff0c 操作系统又能为其解决什么问题呢 xff1f 操作系统和其他电子产品
  • Duktape:一个新的小巧的超精简可嵌入式JavaScript引擎

    原文链接 xff1a http ourjs com detail duktape E4 B8 80 E4 B8 AA E6 96 B0 E7 9A 84 E5 B0 8F E5 B7 A7 E7 9A 84 E8 B6 85 E7 B2 B
  • 解析物联网三大实时协议

    解析物联网三大实时协议 2015 08 25 国家物联网标识管理公共服务平台 实时通信技术作为一项根本性前提 xff0c 在物联网应用程序的开发工作中扮演着核心角色 想象一下 xff0c 如果我们能够利用手机与家居环境内的各种小装置进行通信
  • stm32低功耗定时器lptimer在djyos下的应用

    djyos的tickless模式配合低功耗模块可以支持传感器之类功耗要求极端苛刻的应用 xff0c djyos的系统时基如果能用stm32的lptimer来提供 xff0c 堪称妙绝 xff0c APP就可以做到完全不用操心功耗 xff0c
  • 一起学djyos-罗侍田-专题视频课程

    一起学djyos 2195人已学习 课程介绍 djyos是国内原创的开源操作系统 xff0c 采用类BSD许可证 经历13年的时间考验 xff0c 可靠性高 xff0c 实时性高 xff0c 功能强大 xff0c 广泛用于电力系统 自动控制
  • C#中十进制与十六进制之间的转换

    一 十进制转换为十六进制 int a 61 654 string A 61 a ToString 34 X6 34 上面Tostring 34 X6 34 是将整型a转化成16进制数 xff1b 其中 xff1a xff08 1 xff09
  • RS485波形记录与分析

    异步串行数据的一般格式是 xff1a 起始位 43 数据位 43 停止位 xff0c 其中起始位1 位 xff0c 数据位可以是5 6 7 8位 xff0c 停止位可以是1 1 5 2位 对于正逻辑的TTL电平 xff0c a 起始位是一个
  • 电脑作为服务器+数据库环境构建以及VS中C#远程连接数据库

    一 将作为服务器的电脑连接网络 xff08 以下简称 服务器 xff09 xff0c 查询服务器的ip地址 方法 xff1a cmd命令ipconfig中IPv4地址就是服务器的ip地址 二 测试服务器和用户电脑 xff08 就是另一台电脑
  • t和printf的缓冲机制

    众所周知 xff0c cout和buffer都是有缓冲的 网上很多把cout和printf混用出错归结为一个有缓冲 xff0c 一个无缓冲 xff0c 事实会在下面说明 cout和printf的输出是先从右往左读入缓冲区 xff0c 再从t