原则一:安全性
1.如果遇到需要读入用空格和换行分隔的char,同时伴随其他int等输入,一定要用cin!!!否则大概率会出玄学错误。
2.如果需要用到C++string中的运算符重载等操作,强烈建议用cin!因为scanf不能直接输string,需要预处理一下
相关代码:
#include <stdio.h>
#include <string>
using namespace std;
int main()
{
string a;
a.resize(100); //需要预先分配空间
scanf("%s", &a[0]);
puts(a.c_str());
return 0;
}
3.如果需要处理整行字符串(中间有空格不能用scanf("%s",a);)强烈建议使用getline不用gets()否则有可能出玄学错误(尤其是混有其他输入比如int之类时)
原则二:高效性
C++io流虽然安全性高,但是比scanf慢10倍,数据一大就超时,解决方法有2:
(1)只用C语言io
(2)把endl换成“\n”,加一句cin.tie(0);
cout用了一個類似優化的設計,叫作緩衝區(由作業系統實作),所有的輸出都會先進到緩衝區裡,直到緩衝區滿了才會清空緩衝區並把字串輸出到stdout之類的輸出串流,難怪沒有跟stdout同步會出錯。(解释了后文讲到关同步流以后不能用stdin的原因)
而當一般人寫程式的時候,輸出當然希望程式會把東西印到螢幕上,但是如果緩衝區還沒滿,我們就看不到結果了!
怎麼辦呢?cout有一個物件叫作flush(用法跟endl一樣),做的事情就是強迫清空緩衝區,並輸出到串流。(flush后文会讲到)
但是為什麼平常初學C++的人都沒有打過flush呢?原因有幾個,一個是Windows8以前的Windows CMD會自動清空緩衝區(或是根本沒有QAQ),另外一個主要的原因就是,其實endl就是<<’\n’<<flush;,對,endl就是換行加上flush,也就是說,如果我們用endl的話,就會強迫每個數字都清空緩衝區,累積一定量再一起輸出對cout來說可以優化一些操作,而這樣就破壞了這個優化了
繁体部分转载链接:https://www.jianshu.com/p/5bffe6faaf17
cin.tie()
cin is tied to the standard output stream cout (see ios::tie), which indicates that cout’s buffer is flushed (see ostream::flush) before each i/o operation performed on cin.
没有绑定的输入输出流的i/o操作互相独立,互不影响。cin和cout绑定后,cin之前会将cout输出缓冲区的数据刷新到输出文件中。
这样可以避免cout语句在cin之前,而命令行窗口中cin的读入数据在cout输出数据之前。
也就是说,在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
这样设计的原因:有時候可能要寫一些console應用程式,如果我們要使用者輸入一些值的時候可能要先輸出一些提示訊息像是「請輸入一個數字:」然後才用cin輸入,要是上面那一句話沒有被flush到螢幕上的話,使用者就看不到了,而且你可能不想要換行,就算加<<flush也很麻煩,所以C++就設計了這樣的作法,讓你在cin前會把cout清空緩衝區。
(3)关同步流:
cin慢是有原因的,其实默认的时候,cin与stdin总是保持同步的,也就是说这两种方法可以混用,而不必担心文件指针混乱,同时cout和stdout也一样,两者混用不会输出顺序错乱。正因为这个兼容性的特性,导致cin有许多额外的开销,如何禁用这个特性呢?只需一个语句std::ios::sync_with_stdio(false);,这样就可以取消cin于stdin的同步了。同时带来一个问题:不能用C语言io了,比如scanf\printf\getchar(快读不能用了)\gets.
最终,数据大的时候,要么用scanf,printf,要么用C++的cin和cout关同步流再读入,关同步流以后一定不能用C语言函数了!!!
个人还是习惯全程用关同步C++函数的,同时保证了安全性和高效性,但是——要保证你的队友不用C语言io,否则就会像我们9月26号的比赛那样,检查3小时,wa到最后,比赛结束以后发现是关同步以后队友用了printf!!!
(4)用快读,原理是读取字符再处理成数字比直接读取数字快很多。
inline long long read(){//可以返回int
long long x=0,f=1;
char ch=getchar();
while(ch<‘0’||ch>‘9’){
if(ch==’-’)
f=-1;
ch=getchar();
}
while(ch>=‘0’&&ch<=‘9’){
x=x10+ch-‘0’;
ch=getchar();
}
return xf;
}
关于缓冲区,再介绍一个函数:
flush()
ostream类的一个方法,作用是将缓冲区的数据刷新到输出文件。
/* Flushing files (flush manipulator) */
#include <ostream> /* std::flush*/
#include <fstream> // std::ofstream
#include <bits/stdc++.h>
int main () {
std::ofstream outfile ("test.txt");
for (int n=0; n<100; n++)
{
outfile << n;
_sleep(1000); // 1s
outfile.flush(); //<--
}
outfile.close();
return 0;
}
执行outfile.flush()时,把outfile缓冲区中的数据输出到test.txt中。因此test.txt中每隔一秒新增一个数字。
若是删除outfile.flush()这句,则100s以内test.txt没有增加内容。
执行outfile.close()是outfile缓冲区的数据被一次性flush输出到test.txt中。
因此flush()的作用是将刷新当前缓冲区的数据,将其输出到输出文件中。
结论:从左到右列举安全性和方便性高到速度快的IO方式
cin/cout/getline -> 把endl换成“\n”,加cin.tie(0) -> 关同步流并且不用stdin -> 只用scanf+printf -> 快读加printf
附录:实验结果
1.cin,cout只关同步流,或者关同步以后只加tie,或关同步以后只把endl改成"\n".
2.cin,cout,关同步流,同时加cin.tie(0),同时把endl换成“\n”
3.全换成scanf,printf
4.快读+printf//这个是最快的方法
5.快读+cin+关同步流
6.快读+cin+cout不关同步流
7.关同步流,快读+cout,(有无tie(0)完全没有影响)
**
**
题目链接:https://codeforces.com/contest/1424/problem/J