本节书摘来自异步社区出版社《C和C++代码精粹》一书中的第1章,第1.8节,作者: 【美】Chuck Allison,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.8 标准流
C和C++代码精粹
C++中有4个预定义的流:cin(标准输入),cout(标准输出),cerr(标准错误),clog(标准错误)。除了cerr外其余都是全缓冲流。就像stderr一样,cerr的行为好象是非缓冲的,但事实上它是单元缓冲的,也就是说它在处理完每一个对象而不是每一个字节后会自动清除缓冲。例如,带有单元缓冲的语句:
cerr<<“hello”;
缓冲处理5个字符,然后清除缓冲区。一个非缓冲处理的流会立即发送每个字符到它的最终目的地。
程序清单1.5 通过引用从函数中返回一个对象
// retref.cpp:返回一个引用
#include <stdio.h>
int & current(); // 返回一个引用
int a[4] = {0,1,2,3};
int index = 0;
main()
{
current() = 10;
index = 3;
current() = 20;
for (int i = 0; i < 4; ++i)
printf("%d ",a[i]);
putchar('\n');
}
int & current()
{
return a[index];
}
//输出:
10 1 2 20
下列程序将标准输入拷贝到标准输出:
// copy1.cpp:将标准输入拷贝到标准输出
#include <iostream>
using namespace std;
main()
{
char c;
while (cin.get(c))
cout.put(c);
}
注意到标准头文件名(即iostream)不再使用一个.h的后缀。几乎所有C++标准库中的内容,包括流,都驻留于名字空间(namespace std)中。一个名字空间就是一个包括声明在内的已命名的范围。上面第二行using指令指示编译器在翻译期间查找声明的名字时搜寻std。标准C头文件也存在于C++程序的std标准名字空间中,并以字母c作为前缀。为了包含,可以这样做:
#include < cstdio >
using namespace std;
或用通常的#include<stdio.h>。
一个从流中读取的函数称为提取器(extractor),而一个输出函数称为插入器(inserter)。get提取器从流中把下一个字节存放到它的char引用参数中,像多数流成员函数一样,get返回流本身。当一个流出现在像上面的while循环的布尔型上下文中,如果数据成功传递,它检验为true;如果有错误,则为false。就像试图过了文件尾还要读文件一样。尽管这样简单的布尔型检验在大多数时间能满足,但你可以在任何时候使用下面这些布尔型成员函数对流的状态进行询问:
bad ( ) 严重错误 (流被误用)
fail ( ) 转换错误 (数据不正确但流正常)
eof ( ) 文件尾
good ( ) 上述都不是
下例程序实现逐行拷贝:
// copy2.cpp: 逐行拷贝
#include <iostream>
using namespace std;
main()
{
const size_t BUFSIZ = 128;
char s[BUFSIZ];
while (cin.getline(s,BUFSIZ))
cout << s << '\n';
}
getline提取器读取BUFSIZ-1个字符给s,如果遇到一个换行符就停下来,添加一个空字节,丢弃换行符。输出流使用左移运算符作为插入器。任何对象,无论是系统预定义的还是用户自定义的,都可以是流中插入链的一部分。你必须自己重载运算符<<用于自己的类中。
程序清单1.6是一个说明用>>运算符来实现提取功能的程序。由于在C中,通常使用stderr作为提示(因为它没有被缓冲),就会在C++中使用cerr:
cerr << “Please enter an integer:” ;
cin >> i;
这在C++中不是必需的,因为,cout与cin是绑定在一起的,当输出请求输入时,一个依赖于输入流的输出流被自动地刷新。如果需要强制刷新,可以使用一个flush成员函数。
程序清单1.6 回应值和地址的整型提示符
// int.cpp:为一个整数提示
#include <iostream>
using namespace std;
main()
{
int i;
cout << "请输入一个整数: ";
cin >> i;
cout << "i == " << i << '\n';
cout << "&i == " << &i << '\n';
}
//例子执行结果:
请输入一个整数:10
i == 10
&i == 0xfff4
物理地址是以定义实现的格式打印的,通常是16进制,当然字符数组是个例外,打印的是字符串的值而不是地址。要想打印C类型字符串的地址,得把它转向void * :
char s[ ] = …;
cout << ( void * ) s<< ‘\n’; // 打印地址
操作符>>默认方式是跳过空格。程序清单1.7的程序利用这个特点来计算文本文件的字数。提取字符串操作类似于scanf中的%s格式化标志。在读取字符时,也可以关闭这种跳过空格的方式(见程序清单1.8)。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。