C++重学之路 5 控制语句和逻辑运算符
5 控制语句和逻辑运算符
- 1 计数器控制的循环的要素
- 2 for循环语句
- 应用:计算复利
- 在金融计算中使用double或float类型的特别提醒
- 利用流操纵符格式化数值的输出
- 3 do...while循环语句
- 4 switch多路选择语句
- 读入输入的字符
- 输入EOF指示符
- 在输入中忽略换行符、制表符和空格
- 有关数据类型的说明
- C++11的类内初始化器
- 5 break和continue语句
- break语句
- continue语句
- 6 逻辑运算符
- 逻辑与(&&)运算符
- 逻辑或(||)运算符
- 逻辑非(!)运算符
- 7 运算符优先级总表
- 8 ==运算符和=运算符的混淆问题
- 左值和右值
1 计数器控制的循环的要素
计数器控制的循环需要:
- 命名一个控制变量(或者循环计数器)
- 设置控制变量(controler)的初值
- 定义一个循环继续条件,用于对控制变量终止的测试(例如,循环是否应该继续)
- 增值(或减值),即在每次循环过程中修改控制变量的值
#include<iostream>
using namespace std;int main()
{unsigned int counter = 1;while (counter <= 10){cout << counter << " ";++counter;}cout << endl;
}
也可以用下面的写法让代码更简练
#include<iostream>
using namespace std;int main()
{unsigned int counter = 0;while (++counter <= 10)cout << counter << " ";cout << endl;
}
TIPS:
应使用整数值控制计数器的循环。
自增运算符和自减运算符分别只能和整形操作数一起使用。
2 for循环语句
通常,for语句用来描述计数器控制的循环,而while语句用于表达标记控制的循环。
如果for语句头部的“初始化”表达式中声明了控制变量,那么该控制变量仅能在for语句的循环体中使用,在此for语句之外它是未知的。
对控制变量名的使用进行的限制称为变量的作用域(scope)。
for循环头部如果要定义多变量,只能定义同种数据类型的变量,否则只能在for循环外定义多于变量,若认为浪费变量名,可以加{}
限制作用域。正确定义如下:
for (int i=0 , j = 1; i < 9 && j < 10; i++ , j++)
{//blabla...
}
或
for (int i=0 , j = 1; i < 9 || j < 10; i++ , j++)
{//blabla...
}
for循环头部的三个表达式可选,但两个分号必需。
将分号直接放置在for语句头部的右括号的右边,这是一个逻辑错误。
控制变量的值可以在for语句循环体内进行改变,但容易导致难以察觉的逻辑错误。
若循环控制变量增值或减值的步长超过1,应尽量避免在循环条件中使用相等运算符(!=
或 ==
)。
应用:计算复利
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;int main()
{double amount;double principal = 1000.0;double rate = .05;cout << "Year" << setw(21) << "Amount on deposit" << endl;cout << fixed << setprecision(2);for (unsigned int year = 1; year <= 10; ++year){amount = principal * pow(1.0 + rate, year);cout << setw(4) << year << setw(21) << amount << endl;}
}
函数pow(x,y)
计算x的y次幂的值,它需要接受两个类型为double
的实参,并返回一个double
类型的值。如果这个程序没有头文件<cmath>
,那么将无法通过编译。例子中的变量year
是个整数,这个头文件包含信息,告诉编译器在这个函数调用时,把year的值转换成一个临时的double
值。这些信息含在pow
函数的函数原型中。
如果忘记包含头文件<cmath>
,将会产生一个编译错误。
在金融计算中使用double或float类型的特别提醒
例如,计算机中保存到两笔金额分别为14.234(打印出来是14.23)和18.673(打印出来是18.67),当他们相加时,内部求和的结果是32.907(打印出来是32.91),但是我们自己将打印的数字相加得到的是32.90。应当注意这个隐患。
利用流操纵符格式化数值的输出
在例子中,流操纵符setw(4)
规定了下一个输出值应占用的域宽为4。也就是说,cout
打印一个值,它至少占用4个字符位置。如果输出的值小于4个字符位置的宽度,那么在默认情况下,该值的输出在域宽范围内向右对齐;如果输出的值大于4个字符位置的宽度,那么域宽将向右侧扩展到整个值的实际宽度。
为了指出值要向左对齐输出,只需要简单地输出无参数的流操纵符left
(在头文件<iostream>
中可以找到)即可。当然向右对齐也可以恢复,只是再输出无参数的流操纵符right
而已。
我们在例子中应用了流操纵符fixed
和setprecision
,这些格式的设置如果不被更改则会一直起作用。正因如此,称这样的设置为黏性设置(sticky setting)。可是,指定域宽的setw
只对接下来要输出的值有用。
3 do…while循环语句
do…while语句是循环体执行之后再进行循环继续条件的测试,因此循环体总是至少执行一次。
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;int main()
{unsigned int counter = 1;do{cout << counter << " ";++counter;} while (counter <= 10);cout << endl;
}
注意while语句的括号右侧有分号。
4 switch多路选择语句
使用switch语句优化GradeBook类:
#include <string>class GradeBook
{public:explicit GradeBook(std::string );void setCourseName(std::string );std::string getCourseName() const;void displayMessage() const;void inputGrades();void displayGradeReport() const;
private:std::string courseName;unsigned int aCount;unsigned int bCount;unsigned int cCount;unsigned int dCount;unsigned int fCount;
};
#include <iostream>
#include "GradeBook.h"
using namespace std;GradeBook::GradeBook(std::string) : aCount(0), bCount(0), cCount(0), dCount(0), fCount(0);
{setCourseName(name);
}
// SETTERS
void GradeBook::setCourseName(std::string)
{if (name.length() <= 25)courseName = name;else {courseName = name.substr(0, 25);cerr << "Name \"" << name << "\" exceeds maximum length (25).\n"<< "Limiting courseName to first 25 characters.\n"<< endl;}
}
// GETTERS
string GradeBook::getCourseName() const
{return courseName;
}
void GradeBook::displayMessage() const {std::cout << "Welcome to the grade book for\n"<< getCourseName() << "!\n"<< std::endl;
}
void GradeBook::inputGrades() {int grade;cout << "Enter the letter grades.\n"<< endl<< "Enter the EOF character to end input." << endl;while ((grade = cin.get()) != EOF) {switch (grade) {case 'A':case 'a':++aCount;break;case 'B':case 'b':++bCount;break;case 'C':case 'c':++cCount;break;case 'D':case 'd':++dCount;break;case 'F':case 'f':++fCount;break;case '\n':case '\t':case ' ':break;default:cout << "Incorrect letter grade entered. Enter a new grade."<< endl;break;}}
}
void GradeBook::displayGradeReport() const {cout << "\n\nNumber of students who received each letter grade:"<< "\nA: " << aCount << "\nB: " << bCount << "\nC: " << cCount<< "\nD: " << dCount << "\nF: " << fCount << endl;
}
#include "GradeBook.hpp"int main()
{GradeBook myGradeBook("CS101 C++ Programming");myGradeBook.displayMessage();myGradeBook.inputGrades();myGradeBook.displayGradeReport();
}
读入输入的字符
函数cin.get()
从键盘读入一个字符并保存到整型变量grade
中,一般字符存储在char
类型的变量中,它们也可以存储在任何整数数据类型中,因为类型short
、int
、long
和long long
都可以保证不比char
类型小。
所以根据具体用途,即可以把字符当作整数来处理,也可按照字符来对待。如:
cout << "The character (" << 'a' << ") has the value " << static_cast<int>('a') << endl;
打印字符a和它的整数值,结果如下:
The character (a) has the value 97
一般而言,整个赋值表达式的值正是赋给赋值运算符左边变量。因此赋值表达式grade=cin.get()
的值等于由cin.get()
返回并赋给变量grade
的值。
a = b = c = 0;
这行代码首先计算赋值表达式c = 0
的值(=
运算符是从右到左结合的)。然后,赋值表达式c = 0
的值(是0)赋给变量b,接着,赋值表达式b = (c = 0)
的值(也是0)赋值给变量a。
输入EOF指示符
EOF代表“end-of-file”,是用于标记“文件结束”的一个符号。在程序中我们常使用EOF(一般取值为-1)做标记值,但不可以直接输入-1或者EOF这三个字符作为这个标记值。准确地说,只能输入一个由具体系统决定的代表“文件结束”的组合键,指示不再有数据需要输入了。
EOF是一个符号整数常量,它通过头文件<iostream>
包含到程序中。EOF具有的数据类型是int
。
在OS X/UNIX/Linux系统和很多其他系统中,“文件结束”的输入通过在一行上输入如下的组合键实现:<Ctrl> d
在Microsoft Windows之类的其他系统下,“文件结束”的输入通过在一行上输入如下的组合键实现:<Ctrl> z
某些情况下,必须在按了前述组合键后,再按一次回车键。字符^Z
有时出现在屏幕上,表示文件结束。
在switch
语句中,若在单词case
与被测试的整数值之间遗漏了空格,例如将case 3:
写成了case3:
,那么会引起一个逻辑错误。
在输入中忽略换行符、制表符和空格
case '\n':
case '\t':
case ' ':break;
为了使程序读入字符,必须通过按回车键,这样会在希望处理的字符之后在输入中多加一个换行符。上述语句避免了每次输入换行符、制表符和空格时,都有default
子句打印一条错误信息。
每个case
情况只可用于测试整型常量表达式——字符常量和整数常量的任意组合,其计算结果是一个常整数值。同样,每个case
标签只能指定一个整型常量表达式。
TIPS:
在
switch
语句的case
标签中指定非常量的整型表达式是一个语法错误。在
switch
语句中,如果提供同样的case
标签,则产生一个编译错误。
有关数据类型的说明
C++具有灵活的数据类型长度,每种类型的整数取值的范围由具体平台所决定。
除了int
和char
之外,C++还提供了类型short
、long
和long long
。对于short
整数而言,它的最小取值范围是-32768 ~ 32767。对于绝大多数的整数运算来说,long
整数就足够了。long
整数的最小取值范围是-2147483648 ~ 2147483647。在大多数计算机中,int
类型要么与short
类型等价,要么与long
类型等价。各个int
类型的取值范围至少与short
类型相同,最多与long
类型一样。数据类型char
既可以用来表示计算机字符集中的任何字符,也可以用于表示小整数。
C++11的类内初始化器
C++11允许程序员在类声明中声明数据成员时,为它们提供默认值。如:
unsigned int aCount = 0;
unsigned int bCount = 0;
unsigned int cCount = 0;
unsigned int dCount = 0;
unsigned int fCount = 0;
这与之前例子中在类的构造函数中对数据成员初始化不同。
5 break和continue语句
break语句
break
语句在while
、for
、do...while
或者switch
语句中执行,立刻使程序退出这些语句。
break
语句的常见用法是要么提前离开循环,要么用于跳过switch
语句的剩余部分。
continue语句
continue
语句在while
、for
、do...while
或者switch
语句中执行时,是程序跳过循环体内剩下语句,继续进行循环体下一次迭代。
在while
和do...while
语句中,循环继续条件的测试在continue
语句执行之后马上进行。在for
语句中,则执行增值表达式,然后对循环继续条件进行测试。
在for
循环语句中,当continue
语句执行后,程序控制转到for
头部的控制变量增值部分继续执行。
如果while
语句的增值表达式跟随在continue
语句之后时,该增值表达式在程序测试循环继续条件之前不会执行,很可能导致死循环。
6 逻辑运算符
逻辑与(&&)运算符
若我们希望在保证两个条件都为true的前提下才能选择某个执行路径,则可以使用&&
运算符。
一个逻辑与表达式的右侧只有在其左侧为true的情况下才会被计算。
TIPS:
在数学中的 3<x<7 是正确的表达,但在C++中要写成
3 < x && x < 7
。
逻辑或(||)运算符
&&
运算符的优先级比||
稍高些。两个运算符都是从左到右结合的。
与&&
一样,含||
的表达式,其计算在一旦能够确定整个表达式的真假时,就会立即结束。
逻辑非(!)运算符
一元的逻辑非运算符只用一个条件作为操作数,且放在要“逆转”的条件前面。如:
if(!(grade == sentineValue))
大多数情况下,对于用逻辑非运算符表达的条件,程序员同样可以通过合适的关系运算符或者相等运算符来表达。例如,上面语句可以写成:
if(grade != sentineValue)
7 运算符优先级总表
在上一章的总表中加入逻辑和逗号运算符。
运算符 | 结合律 | 类型 |
---|---|---|
:: ()
|
从左向右 | 最高 |
++ -- static_cast<类型>()
|
从左向右 | 后缀 |
++ -- + - !
|
从右向左 | 一元(前缀) |
* / %
|
从左向右 | 乘 |
+ -
|
从左向右 | 加 |
<< >>
|
从左向右 | 插入/提取 |
< <= > >=
|
从左向右 | 关系 |
== !=
|
从左向右 | 相等 |
&&
|
从左向右 | 逻辑与 |
||
|
从左向右 | 逻辑或 |
?:
|
从右向左 | 条件 |
= += -= *= /= %=
|
从右向左 | 赋值 |
,
|
从左向右 | 逗号 |
8 ==运算符和=运算符的混淆问题
在赋值时用==
运算符,在比较时用=
运算符,都属于逻辑错误,这种互换具有破坏性,因为通常不产生语法错误。
TIPS:
在书写
x==7
之类的条件时,通常将变量名放左边,常量放右边。但写成7==x
这种形式会好些,即将常量放左边,变量名放右边,因为当不小心用=
代替了==
,编译器将起到保护作用。编译器会认为7=x
是个编译错误,因为常量值是不可更改的。
左值和右值
变量名称为左值(lvalue),因为可以在赋值运算符左边使用;常量称为右值(rvalue),因为只能在赋值运算符右边使用。
左值可以作右值,但右值绝不能作左值。
如果想用一下一条简单语句为一个变量赋值:
x = 1;
却写成:
x == 1;
这里不是一个语法错误。编译器只是简单判断这个条件表达式。表达式的值为true或false,不管怎样,这里没赋值运算符,所以这个值自然丢失了,同时x的值保持不变。
C++重学之路 5 控制语句和逻辑运算符相关推荐
- C++重学之路 4 控制语句、赋值、自增和自减运算符
4 控制语句.赋值.自增和自减运算符 1 算法 2 伪代码 3 控制结构 C++中的选择语句 C++中的循环语句 4 if选择语句 5 if...else双路选择语句 条件运算符(?:) 嵌套的if. ...
- C++重学之路 1 计算机和C++简介
1 计算机和C++简介 1.1 硬件和软件 1.1.1 摩尔定律 1.1.2 计算机的组成 1.2 数据的层次结构 1.3 机器语言.汇编语言和高级语言 机器语言(Machine Language) ...
- 重学之路--Redis(1)
基于狂神视频的笔记 Nosql概述 为什么Nosql 原来的架构–单机Mysql Memcached(缓存)+mysql+垂直拆分(读写分离) 减轻数据的压力 使用缓存来保证效率 分库分表 +水平拆分 ...
- 重学之路-Redis(2)
基于狂神视频做的笔记 事务 Redis 事务本质:一组命令的集合!事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行! 一次性,顺序性,排他性!执行一系列命令! Redis事务没有隔离级别 ...
- 重学前端学习笔记(八)--JavaScript中的原型和类
笔记说明 重学前端是程劭非(winter)[前手机淘宝前端负责人]在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系,笔者主要整理学习过程的一些要点笔记以及感悟,完整的可以加入winter的专 ...
- 从底层重学 Java 之 BigInteger 大整数 Gitchat连接
Gitchat连接 https://gitbook.cn/gitchat/activity/5f395a80aced402379f6a0ca 简介 从底层,从原理,我们来重学一次 Java.BigIn ...
- css html 双面打印_CSS语法与规则 — 重学CSS
我是三钻,一个在<技术银河>中等你们一起来终生漂泊学习. 点赞是力量,关注是认可,评论是关爱!下期再见 ! 前言 进入重学 CSS 的第一步,首先需要找到一些线索.我们在前面的课程中讲学习 ...
- 重学Java设计模式-创建者模式-建造者模式
重学Java设计模式-创建者模式-建造者模式 内容摘自:重学 Java 设计模式:实战建造者模式「各项装修物料组合套餐选配场景」 | bugstack 虫洞栈 建造者模式介绍 图片来自:https:/ ...
- 《重学设计模式》PDF 出炉了 - 小傅哥,肝了50天写出18万字271页的实战编程资料...
持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥 博客:https://bugstack.cn ❝ 沉淀.分享.成长,让自己和他人都能有所收获!???? ❞ 目录 一.前言 二.简介 1. 谁发明了设 ...
最新文章
- --single-transaction 参数对应MyISAM引擎和InnoDB引擎
- R语言多因素方差分析及评估假设检验
- 手把手pytorch-transformers实战
- SSH-KeyGen 的用法 【转载】
- 发现四川科技馆在线网站修改用户设置页面的一个问题
- html优美界面左侧下拉,一组时尚的侧边栏菜单和下拉列表UI设计
- 修改之前的myshell使之支持输入输出重定向
- [前端随笔][css] 弹性布局
- nvsip能用别的软件吗_为什么很多企业转向了 Golang? Java, Python, C#没落了吗?
- 一文搞懂注册中心 zookeeper 和 eureka 中的CP和 AP
- linux上python3的安装
- Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结
- 深入解析数码相机CCD坏点及噪点检测!【图解教程】
- AD转换原理,器件与参数
- Scratch编程学习的好处
- python绘制国际象棋规则口诀_学好国际象棋必须知道的小口诀技巧
- 物联网常用无线模块 接收灵敏度及发射功率简化测量方法
- curl 命令的使用
- 传递给Appium服务器以开启相应安卓Automation会话的Capabilities的几点说明
- 你真的了解抑郁症吗?