1.048596

关于不同IO函数和缓冲

比赛中无意发现了问题,但是如果不知道原因也无从修改,所以了解了cincout同标准输入输出的同步问题和效率问题。

问题产生

当使用了ios::sync_with_stdio(false)之后发现本地正常的程序提交后并不能通过测试,原因在于取消同步之后的标准输出和流式输出并不能够混用从而造成混乱。当然这与评测的运行环境有关,有些编译器则能够将这一问题解决在编译阶段。

取消同步后printfscanf不能和cincout同时混用

拓展问题

对于发现的现象不想终结于吃一堑长一智,因为这样试错的解决问题积累经验成本太大,如果能够知道其中的原理就能够解决类似的问题而不是“深度优先搜索”的尝试。

输出流和缓冲

在编程调试过程中常常能发现cout输出并没有立即输出,是因为它具有缓冲区。

int main() {
  printf("Hello World!");
  _Exit(0);  // Force the program to end immediately.
}

上面一端代码在不同平台上运行结果不同,在Linux平台上运行没有字符输出到控制台上。这是因为输入输出都有一定的缓冲并不会立即生效,但如果直接强制结束程序那么缓冲区中的输出就没法正常释放。

  • 清空缓存区
cin.clear();
// Reset the cin condition state.
cin.ignore(numeric_limits<streamsize>::max());
// Ignore the remaining input in the buffer and increase size of the buffer.

cout和有返回值函数结合

为了减少代码量经常把有返回值的函数放进cout输出语句中,有时候会导致函数的调用错误的函数重载或返回值精度问题。

// unsafe use.
cout << ceil(a) << endl;
// safe use.
int b = ceil(a);
cout << b << endl;

同步输入输出流

对于sync_with_stdio这个函数是C++为了兼容C语言中的输入输出采用的手段,默认是开启的,使输入输出流绑定到一起不出现混乱。如果关闭之后就不能够实现同步了,两种输入输出混用就会出现错误!

关于cin的使用

正因为cin函数是直接从缓冲区取数据的,所以有时候当缓冲区中有残留数据时,函数会直接取得这些残留数据而不会请求键盘输入减少了系统中断。它非常的智能能够自动判断读入的类型和读入方式,自动省略换行和空格,但是相较于scanf要慢一些。

读入速度讨论

一般情况下cin的速度要比scanf慢很多,如果关闭同步之后可以把他们的速度差距缩小到两倍以内(视平台而定),当然对于整数还有更加快速的方法来读入。

// Single reading of characters is faster than reading numbers.
long long read() {
  long long x = 0;
  bool f = 0;
  char c = getchar();
  while (c < '0' || c > '9') {
    if (c == '-') f = 1;
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    x = (x << 1) + (x << 3) + (c ^ 48);
    c = getchar();
  }
  return f ? -x : x;
}

输入函数

下面介绍了集中输入函数的使用,最后两个需要着重区分,前者属于<istream>针对字符数组而后者属于<string>自带的函数针对字符串对象。

  • cin.get()

读入单个字符,类似于getchar()的用法,也可以用一个字符数组作为参数直接读入一行文本。

// read a single character
char ch;
ch = cin.get();
cin.get(ch);
// read a line of characters
char str[100];
cin.get(str, 100);
  • cin.getline()

针对字符数组读入整行字符,需要提供最大长度。另外还可以在参数中加入读入结束标记符,默认是\n也可以改成其他标志性的字符。

char str[100];
cin.getline(str, 100);
  • getline()

与上面不同的是,这个函数将针对面向字符串对象,读入一整行结尾都是加入null结束符。

string str;
// Let the cin object and the string to be read as parameters.
getline(cin, str);