Witheart... Follow your heart...
目录
Witheart... Follow your heart...
使用文件尾(EOF: end of file)代替哨兵字符结束输入
前言
本系列文章作为笔者学习C++的一个记录,希望可以坚持下去。目前使用C++Primer Plus这本书。
本系列文章适合有C语言基础的读者阅读。
学习内容
入口条件循环
while 和 for 一样,都是入口条件循环。即是在循环体执行之前就会进行一遍条件的测试;
do.. while 是出口条件(exit condition)循环。
for 循环和 while 循环几乎是等效的。
字符串末尾标记
不同于 C 风格字符串,string 对象不使用空字符来标记字符串末尾。
延时函数 clock()
返回程序执行开始后所用的系统时间,需要包含 ctime。
该函数返回的时间单位不一定是秒。
上述的头文件中定义了符号常量 CLOCK_PER_SEC,该常量等于每秒钟包含的系统时间单位数,所以将系统时间除以这个值,可以得到秒数;或者将秒数乘以该符号常量,可以得到以系统时间单位为单位的时间。
clock() 的返回值的类型是 clock_t。
延时函数参考 STM32 中使用 systick 进行延时的函数。
基于范围(range-based)的 for 循环
用于对数组、容器类的每个元素执行相同的操作。使用方法如下:
double price[5] = {1.2,3.4,5.6,7.8,9.0};
for(double x : price) //将数组中的每个元素都打印出来
cout << x << endl;
但是上述的代码不能在循环体里面对数组中元素进行修改,要修改数组的元素,应该使用引用变量:
double price[5] = {1.2,3.4,5.6,7.8,9.0};
for(double &x : price) //将数组中的每个元素都打印出来
x = x * 0.8;
数组也可以替换为初始化列表:
for(double x : {1.2,3.4,5.6,7.8,9.0}) //将初始化列表中的每个元素都打印出来
cout << x << endl;
使用原始的 cin 进行输入(忽略空白)
如果使用循环和原始的 cin >> x 读取来自键盘的输入,那么怎么知道用户结束了输入呢?
原始的方法是使用哨兵字符(sentinel character),将其作为停止的标记。如下:
cin >> ch;
while(ch != '#'){
cout << ch; //回显用户按下的按键
cin >> ch; //获取下一个键盘输入
}
注意,该程序在输出的时候会省略空格,原因是读取 char 值时,cin 将忽略空格和换行符。
并且,发送给 cin 的输入将被缓冲。这意味着只有当用户按下回车键后,输入的内容才会被发送给程序一起处理。这也是在用户进行输入时,可以在 # 后面输入字符的原因,虽然程序将在遇到 # 后结束对输入的处理。
使用 cin.get(char) 进行输入(不忽略空白)
cin.get(ch) 读取输入中的下一个字符(即使是空格),并把它赋给变量 ch。使用这个函数替换上面的代码就可以进行不忽略空格的输入。
注意,此函数的参数是引用类型,不需要传递指针。
使用文件尾(EOF: end of file)代替哨兵字符结束输入
检测文件尾。C++ 输入工具和操作系统协同工作,检测文件尾并且告知程序。
看起来文件的结尾和键盘输入的结束并没有太大的关系,但实际上有一些深层的关系。
首先,重定向。很多操作系统都支持重定向,允许使用文本代替键盘输入;其次,很多操作系统都允许使用键盘来模拟文件尾条件。(在 Windows 命令提示符模式下,可以在任意位置按 Ctrl + Z + ENTER)
有了以上这些条件,那么我们可以在输入时用键盘输入模拟文件尾来结束输入。具体做法如下:
检测到 EOF 后,cin 将两个标志位(eofbit 和 failbit)都置1。可以通过成员函数 eof() 来查看 eofbit 是否被设置;如果检测到 EOF,则 cin.eof() 将返回 ture。如果 eofbit 或者 failbit 被设置为1,则 cin.fail() 返回 ture。这两个成员函数应该放在读取后。
cin.get(ch);
while(cin.fail() == false){
cout << ch; //回显用户按下的按键
cin.get(ch); //获取下一个键盘输入
}
注意,cin 方法检测到 EOF 后,标志位挂起,cin 将不再读取输入,即使再次调用 cin。使用 cin.clear() 可以清除 EOF 标志位。
下面对上面的输入代码进行简化:
1使用 !
while(!cin.fail())
2使用 cin 返回的布尔值
cin.get(ch) 的返回值是一个 cin 对象,但是 istream 类提供了一个可以将 istream 对象(如 cin)转换为 bool 值的函数;当 cin 出现在需要 bool 值的地方,如循环的测试条件中,转换函数将自动被调用,将返回值转换为 bool 值。所以,当最后一次读取成功了,则转换得到的 bool 值为 ture。简化代码如下:
while(cin.get(ch))
这比 fail() 和 eof() 更通用,因为它可以检测到其他失败原因,如磁盘故障。
由于 while 是入口条件循环,因此,while 前不需要再调用一次 cin.get(ch),进入循环时就会先读取输入。
不带参数的 cin.get()
返回输入中的下一个字符,将字符编码作为 int 值返回,与 C 语言中的 getchar() 类似;而接受一个参数的 cin.get(ch) 返回的是一个对象,而不是字符编码。
同样的,有类似于 putchar(ch) 的 cout.put(ch),只不过后者的参数类型是 char,而不是 int。要注意的是,C++标准要求 put(ch) 只有一个原型,参数为 char,当传递其他类型的参数时,将被强制转换。但实际上有一些 C++ 实现提供了三个原型,有 char、signed char、unsigned char,在这些实现中传递其他类型的参数将会报错,因为有三种选择,隐式类型转换不知道将传递的参数转换为哪一种类型,这时候使用显式类型转换可以解决这个问题。
使用 cin.get() 与 EOF 配合来结束输入
当 cin.get() 到达 EOF 时,函数将会返回符号常量 EOF,该常量是在 iostream 中定义的,其值通常为 -1,因为 ASCII 码都不为负数,这样才不会有和其他字符编码冲突的可能。
注意,在有些系统中,char 类型是没有符号的,因此为负数的 EOF 不可能被赋值给代码中存储字符的 char 类型变量,而 cin.get() 返回的又是有符号的 int 型,因此要把字符定义为 int 型,并在输出回显时将其显示转换为 char 型,使显示的是字符而不是字符编码。代码如下:
int ch;
while ((ch = cin.get()) != EOF)
{
cout.put(char(ch));
}
逻辑表达式
顺序点
C++ 规定,|| 运算符是个顺序点(sequence point)。运算符左边的表达式中修改值的操作将先被执行,再判定右边的表达式。
逻辑运算符的其他表达
标识符 and、or、not 都是 C++ 保留字,可以代替逻辑运算符。不可以作为变量名使用,但它们不是关键字,因为它们只是已有的语言特性的另外一种表示对的方式。在 C 语言中,它们不是保留字,但在包含头文件 iso646.h 后,可以将它们用作运算符。
字符函数库 cctype
这个库是 C++ 从 C 语言继承来的,是有关字符操作的库。可以判断字符是否为字母或者数字,是大写字母抑或小写字母等等操作,省去了使用逻辑运算符进行判断的麻烦。
switch..case 注意
switch 语句中的每一个 case 标签都必须是一个单独的值,必须是整数或者 char,不能是浮点数,必须是常量。