重要提醒: 标准I/O库会设置缓冲的, 但系统调用(如:write和read)是没有缓冲区的.
测试代码
//用标准I/O库中的函数getc,putc实现标准输入复制到标准输出
int main(void){
int c;
while((c = getc(stdin)) != EOF){ //任何时候只要要求一个从不带缓冲的流或带行缓冲的流中得到数据, 但数据已经被取空时, 即流链接的文件为空时, 进入等待, 那么就会冲洗所有输出流
//将从标准输入链接的文件中读到的换行符忽略, 不写到输出缓冲区中
if(c == '\n'){
//sleep(3); //如果sleep打开, 就不会立刻重新回到while判断, 首先换行符不会输入到输出缓冲区中, 不会使得输出缓冲区刷新, 其次, sleep的这10秒不会要求从标准输入流中得到数据, 即不会触发冲洗所有输出流, 所以此时数据全在输出缓冲区中, 不会写道标准输出链接的文件中
continue; //如果slee不打开, 而是立刻执行continue, 那么就会重新回到while判断, 进而再次执行getc, 即要求了从带行缓冲的标准输入流得到数据, 但数据已经被取空, 即标准输入链接的文件为空, 就会引发冲洗所有输出流, 这也就是为什么换行符没有写入标准输出链接的文件中, 但标准输出仍然输出内容
}
//放入输出缓冲区, 由于输出缓冲区也是行缓冲, 所以当缓冲区中有换行符'\n'时, 才会将标准输出缓冲中的数据写到标准输出链接的文件中
if(putc(c,stdout) == EOF){
fprintf(stderr,"putc put c failed: %s.\n",strerror(errno));
exit(1);
}
sleep(1);//每隔1秒才回到getc读取标准输入流链接的文件中的数据, 只要有标准输入的文件中还有数据, 就不会触发冲洗所有输出缓冲区, 所以不会出现每1秒标准输出出现一个字符
}
if(ferror(stdin)){ //真错
fprintf(stderr,"getc failed: %s.\n",strerror(errno));
exit(1);
}
else{ //没数据了
fprintf(stdout,"EOF.\n");
exit(0);
}
}
测试结果及分析
-
标准输入的缓冲区刷新策略:
标准输入的缓冲去区策略比较好理解. 运行程序后, 在输出设备上键入字符但并未键入回车前, 已经键入的字符会被保存在标准输入的缓冲区中, 并未写到标准输入链接的文件中; 当键入回车时, 由于标准输入的缓冲区是行缓冲, 所以会将缓冲区中的这一行数据刷新写入到标准输入链接的文件中, 标准输入的链接的文件中此时有了数据, getc开始工作, 每次取一个字节(一个字符), 直到取到文件末尾 -
标准输出的缓冲区刷新策略:
跟标准输入一样, 标准输出也是行缓冲的文件流, 所以缓冲区如果有换行符’\n’就会引发刷新缓冲区, 将内容写入到标准输出;
但是, 如果不将’\n’写入到标准输出的缓冲区中, 标准输出会不会刷新呢, 结果是标准输出也会刷新, 这是为什么呢, 根据apue书上的内容和合理的推测, 目前的结论是:任何时候只要要求一个从不带缓冲的流或带行缓冲的流中得到数据, 但数据已经被取空时, 即流链接的文件为空时, 进入等待, 那么就会冲洗所有输出流
. 按照这个结论, 试着解释一下为什么此时行缓冲区没有换行符但仍然被刷新了, 这是因为当标准输入被取空后, 再次执行getc时, 即要求了从带行缓冲的标准输入流得到数据, 但数据已经被取空, 即标准输入链接的文件为空, 就会引发冲洗所有输出流, 这也就是为什么换行符没有写入标准输出链接的文件中, 但标准输出仍然输出内容.