目录
1. 变量
影院售卖机
学习变量,实现售卖机功能第一步。
计算机的内存
内存:计算机用来记忆,存储信息的硬件。
操作系统一般会把内存划分成不同区域来使用。
将内存想象成一个充满盒子的仓库。
内存中每个基本存储单元可以存放一个字节的数据,每个字节具有8位,也就是8个比特(bit)。
每个内存单元有一个唯一的地址,常用一个16进制数表示和区分。
变量的声明
变量是专门用来存储数据的容器,会根据数据类型决定容量大小。
比如可乐与爆米花的数量需要使用变量来记录,支付总额也需要一个变量来表示。
这些变量都是整数类型,所以容量为一个整数的大小。
变量对应着内存中的一个地址,用于计算时数据的存储和读取 。
比如修改可乐与爆米花的数量时,我们需要在内存中找到对应的变量,并改变其中的数据
变量的声明就是向内存申请“盒子”(内存)的过程。一般的声明方式为 数据类型 变量名称。
代码中的 int(integer)用来明确变量的数据类型
代码中的 coke、popcorn 是用来区分不同变量的变量名称
// 导入系统输入输出头文件 iostream
#include <iostream>
// 使用标准命名空间 std
using namespace std;
int main() {
// TODO 声明可乐数量变量 coke
int coke;
// TODO 声明爆米花数量变量 popcorn
int popcorn;
// TODO 声明消费总额变量 money
int money;
return 0;
}
变量命名规则
数字不可以出现在第一个位置上;
例如:coke330合法,但是330coke以数字作为开头,因此不合法。
在C++中,标识符中的英文字母是区分大小写的;
例如:coke330和Coke330表示的是完全不同的变量名称,即表示不同的标识符。
C++的关键字(保留字)不可以⽤做标识符;
例如:pout合法,但是cout是C++的关键字,不可以用作标识符,因此不合法 Tips: 无需背诵所有关键字
变量的初始化
// 声明消费总额变量 money,并且初始化数值为 0
int money = 0;
代码中的符号"="是赋值运算符
操作是将符号"="右边的数值存放到 左边 的变量中
// 导入系统输入输出头文件 iostream
#include <iostream>
// 使用标准命名空间 std
using namespace std;
int main() {
// TODO 声明消费总额变量 money,并且初始化数值为 0
int money = 0;
return 0;
}
变量的输出
//将初始化好的消费总额打印在屏幕上,并且进行换行
cout << money << endl;
输出变量的时候,我们可以注意以下几点
变量money与"Hello, World!"一样,也可以作为输出内容
输出的内容是变量中保存的数值,而不是变量名称
// 导入系统输入输出头文件 iostream
#include <iostream>
// 使用标准命名空间 std
using namespace std;
int main() {
// 定义消费总额变量 money,并且初始化数值为 0
int money = 0;
// TODO 将初始化好的消费总额打印在屏幕上,并且进行换行
cout << money << endl;
return 0;
}
总结
// 导入系统输入输出头文件 iostream
#include <iostream>
// 使用标准命名空间 std
using namespace std;
int main() {
// 声明可乐数量变量 coke
int coke;
// 声明爆米花数量变量 popcorn
int popcorn;
// 声明消费总额变量 money,并且初始化数值为 0
int money = 0;
// 将初始化好的消费总额打印在屏幕上,并且进行换行
cout << money << endl;
return 0;
}
练习题
- 阅读以下代码,若想要输出a+b的结果,____处应当填入?
const int a = 10;
int b = 10;
____
常量a不可被修改。
2. 常量
字面量-装在变量里的常量
内存可以看作一个充满盒子的仓库,每个盒子可以储存计算时用到的数据,称为一个变量。例如:用户需要的可乐数量、爆米花数量以及总金额等。
计算机如何定义存放在变量中的数据?
字面量就是存放在变量中的数据。
字面量经常会被装到变量中使用,这个操作叫做赋值,一般写法为 变量名 = 字面量。
// 小戴需要2瓶可乐,3个爆米花
coke = 2;
popcorn = 3;
代码中的符号"="是赋值运算符
操作是将符号=右边的数值存放到左边的变量中
#include <iostream>
using namespace std;
int main() {
int coke, popcorn;
// TODO 小戴需要2瓶可乐,3个爆米花
coke = 2;
popcorn = 3;
return 0;
}
整数和浮点字面量-数学计算中的常量
整数字面量是没有任何小数或指数部分的数字字面量,与数学中的整数直接关联。
十进制:直接写出的整数。例如:0,23,-1024
八进制:以数字0开头的整数,有效数字为0~7,并且不带有正负号。例如:0126,0163
十六进制:以0x或0X开头的整数,有效数字为0~9、A~F(a~f),并且不带有正负号。例如:0x12a,0xf39
浮点字面量是有分数形式或指数形式的数字字面量,与数学中的小数直接关联。
小数形式:由数字和小数点组成。例如:0.66,30.0,14.15926
指数形式:在数学中,一个可以用幂的形式来表示的形式。在C语言中,则以e或E后跟一个整数来表示以10为底数的幂数。
e或E的左侧必须要有数字
e或E的右侧为幂次的数值,必须为整数
上述小数可以等价表示为:6.6E-1,30.0e0,0.01415926E3
字符字面量-表达字符的常量
单字符字面量:
通过将单个字符括在单引号内来创建字符字面量。
例如:'a','m','F','2','}'等。
转义符字面量:
使用''开头的转义字符序列来表示一个特殊字符。
`'\n'`表示换行符;
`'\t'`表示制表符;
`'\''`表示单引号字符(不被解析成字符外的单引号);
`'\\'`表示真正的斜杠字符。
字符串字面量-构成输出的常量
字符串字面量是由一对双引号括起来的字符序列
字符串中可以包含类似于字符字面量普通字符以及转义字符
字符串除了存储所包含的字符外,还额外包含一个结束标志'\0'
'\0':结束符; ASCII编码对应数值为0的字符
练习:
输出下列:
****
****
****
#include<iostream>
using namespace std;
int main() {
// TODO 输出三行内容,每行为4个*和一个换行符
cout << "****\n";
cout << "****\n";
cout << "****\n";
return 0;
}
符号常量-具有名称的常量
在C++中,随着程序运算不改变的量叫做符号常量,可以使用const关键字来定义并且初始化:
const int kCokePrice = 5;
const int kPopcornPrice = 10;
const是⼀个修饰符,加在数据类型int的前⾯,用来给这个变量加上一个不变的属性
初始化时也可以将修饰符const与数据类型int调换位置,写成int const kCokePrice = 5;
总结代码
#include<iostream>
using namespace std;
int main() {
// 字面量
cout << 1 << " 是一个整型字面量" << endl;
cout << 0.5 << " 是一个浮点型字面量" << endl;
cout << 'a' << " 是一个字符型字面量" << endl;
cout << "这句话就是一个字符串型字面量\n" << endl;
// 定义符号常量表示可乐单价
const int kCokePrice = 5;
// 定义符号常量表示爆米花的单价
int const kPopcornPrice = 10;
// 将可乐与爆米花的单价分别打印在屏幕上,并且加上提示信息
cout << "CokePrice is: " << kCokePrice << endl;
cout << "PopcornPrice is: " << kPopcornPrice << endl;
return 0;
}
课后题
- 下列关于常量说法正确的是____。
选项A字符常量只能包括一个字符
选项B"true"是字符串字面量
3. 数据类型
什么是数据类型
在C++的变量声明中,变量名之前需要指定数据类型。
数据类型 变量名称;
数据类型将会决定一个变量存储什么样的信息,从而决定变量的容量有多大,在内存中需要分配多大的空间。
C++中的数据类型分为基本类型和复合类型:
其中基本类型包括了整数、浮点数
复合类型是在基本类型的基础上创建的,包括数组、字符串以及结构体等
整数类型
整数类型分为数值整数类型和字符类型
数值整数用来表示没有小数部分的数字。
数值整数可以按照占用内存大小分为short、int、long以及long long这四种,占用内存越大的类型能表示的数值范围就更大。
同时又可以按照是否表示负值分为有符号版本和无符号版本
字符类型则专门用来存储计算机中的基本符号:英文字母、数字以及标点等。
整数类型-数值整数类型
表示范围
在数学中,整数的范围是无穷无尽的,但是计算机的内存是有限的,所以一种类型能表示的整数范围也是有限的。因此,在C++中,我们需要根据需求,选择合适的数值整数类型。
short、int、long以及long long这四种数值整数类型占用的字节数递增,所以能表示的整数范围也递增。
short类型至少占据2个字节,即16位;一般占用2字节;
int在现代系统中一般占用4个字节,即32位;类型长度大于等于short类型;
long类型长度至少占据4个字节,且大于等于int类型;一般占用4个字节;‘
long long类型长度至少占据8个字节,且大于等于long类型;一般占用8个字节。
计算机内存采用二进制的存储方式,每一个位可以表示0与1两种状态,因此占据nn 位的内存块,可以表示 2^n2个不同的数字。
比如一个 8 位的内存块,可以表示 2 的 8 次方个不同的组合,也就是说能表示 256 个不同的整数;
32 位的int类型,可以表示 2^{32}=4294967296232=4294967296 个不同的整数。
每个类型数据可以分别指定有符号版本和无符号版本,用来明确该类型是否需要表示负值。
比如unsigned int就表示无符号的int类型,只能表示正值;
signed int就表示有符号的int类型,可以表示负值。
不指定有无符号时,都默认是有符号版本。
如果是无符号版本,那么一个8位的内存块可以一一对应到0~255之间的整数;
如果是有符号版本,那么就会考虑负数,这个8位的内存块可以表示一128~127之间的整数。
整数类型-数值整数类型
short price = 500; // 单价
int coupon = -2000; // 优惠
long total = 48000; // 总价
如果输入:
short total = 48000; // 总价
cout << "总价为:" << total << "元。\n";
输出:
总价为:-17536元。
Tips: 这是由于short默认状态下是符号类型,占据16位内存,数据范围在-32768~32767之间,所以无法正确表示48000。
默认状态下short等四个整数类型都是符号类型,这使得它们能够表示的最大数值减小了一半左右。
在确定变量的数值不会是负数的情况下,我们可以利用unsigned关键字,加在原有数据类型之前,创建无符号的整数类型。
unsigned short bike = 6; // 单车数
unsigned int car = 58000; // 汽车数
unsigned long people = 16825000; // 总人数
如果使用无符号short类型定义总价total变量,我们运行以下代码:
unsigned short total = 48000; // 总价
cout << "总价为:" << total << "元。\n";
输出:
总价为:48000元。
#include <iostream>
using namespace std;
int main() {
// TODO 声明 short 类型的变量 total_1,并初始化为48000
short total_1 = 48000;
cout << "总价为:" << total_1 << "元。\n";
// TODO 声明 unsigned short 类型的变量 total_2,并初始化为48000
unsigned short total_2 = 48000;
cout << "总价为:" << total_2 << "元。\n";
return 0;
}
整数类型-字符整数类型
字符类型char是另一种特殊的整数类型,它专门用来存储计算机中的基本符号:英文字母、数字以及标点等。
// 用字符常量初始化一个 char 类型
char size_1 = 'L';
// 用整数常量初始化一个 char 类型,字符L的ASCII编码值为76
char size_2 = 76;
字符整数类型虽然存储方式和数值整数类型相似,都是存放一个数值,但是在输出显示时是不同的。
输入:
// 初始化一个 int 类型
int length = 76;
// 用字符常量初始化一个 char 类型
char size_1 = 'L';
// 用整数常量初始化一个 char 类型,字符L的ASCII编码值为76
char size_2 = 76;
cout << "衣服的长度为:" << length << "厘米。\n";
cout << "衣服的大小为:" << size_1 << "号。\n";
cout << "衣服的大小为:" << size_2 << "号。\n";
输出:
衣服的长度为:76厘米。
衣服的大小为:L号。
衣服的大小为:L号。
#include <iostream>
using namespace std;
int main() {
// 初始化一个 int 类型
int length = 76;
// TODO 用字符常量初始化一个 char 类型 size_1 为L
char size_1 = 'L';
// TODO 用整数常量初始化一个 char 类型 size_2 为L,字符L的ASCII编码值为76
char size_2 = 76;
cout << "衣服的长度为:" << length << "厘米。\n";
cout << "衣服的大小为:" << size_1 << "号。\n";
cout << "衣服的大小为:" << size_2 << "号。\n";
return 0;
}
浮点类型
表示精度
计算机用浮点数表示两类数:
带小数部分的数字; 例如:圆周率3.14、黄金分割比例0.618等,这些数字在整数之间
数值非常大的数字。 例如:宇宙中原子个数约10的80次方,这个数字已经无法被long long整型表示
C++中的浮点数分为三种类型:float、double以及long double,分别表示不同的精度。
浮点数的精度在于它可以表示的有效位数以及指数范围。
指数范围指的是可以表示的指数幂次大小;
由于浮点数更多的应用是用来表示带小数的数字,所以我们主要从有效位数的角度讲解精度。
有效位数用来描述浮点数值的刻画精确程度。
例如:3.14的有效位数是3位,3.1415926的有效位数是8位。
Tips: 需要注意的是,有效位数不会因为小数点的改变而改变,无论314000或者3.14000,它们的有效位数都是3位,多出来0可以看作是一种占位符,因为实际有意义的数字只有3个:3、1和4。
在三种浮点类型中,更大的内存空间可以表示更多的有效位数:
float类型通常占用4个字节,有效位数为6位
double类型占用的空间是float类型的两倍,即8个字节,有效位数为15位
long double类型一般占用16个字节的空间
float ratio = 0.618; // 黄金分割比例
double pi = 3.1415926; // 圆周率
long double atom = 1e80; // 宇宙中原子个数
如果输入:
float pi = 3.1415926; // 圆周率
printf("圆周率为:%.7f。\n", pi); // 输出浮点数的7位小数
输出:
圆周率为:3.1415925。
Tips: 这是由于float类型只能确保6个有效位数的精确显示,最后的数字5是系统随机表示的。想要完整显示8个有效位的圆周率,我们需要使用double类型的变量进行初始化:
double pi = 3.1415926; // 圆周率
printf("圆周率为:%f。", pi); // 输出浮点数的7位小数
输出:
圆周率为:3.1415926。
#include <iostream>
using namespace std;
int main() {
// TODO 声明并初始化一个float类型的变量 pi_1 为 3.1415926
float pi_1 = 3.1415926;
printf("圆周率为:%.7f。\n", pi_1); // 输出浮点数的7位小数
// TODO 声明并初始化一个double类型的变量 pi_2 为 3.1415926
double pi_2 = 3.1415926;
printf("圆周率为:%.7f。\n", pi_2); // 输出浮点数的7位小数
return 0;
}
总结代码
#include <iostream>
using namespace std;
int main() {
// 数值整数类型的表示范围
short total_1 = 48000;
cout << "总价为:" << total_1 << "元。\n";
unsigned short total_2 = 48000;
cout << "总价为:" << total_2 << "元。\n";
// 字符类型的初始化
int length = 76;
char size_1 = 'L';
char size_2 = 76;
cout << "衣服的长度为:" << length << "厘米。\n";
cout << "衣服的大小为:" << size_1 << "号。\n";
cout << "衣服的大小为:" << size_2 << "号。\n";
// 浮点类型的有效位数
float pi_1 = 3.1415926; // 圆周率
printf("圆周率为:%.7f。\n", pi_1); // 输出浮点数的7位小数
double pi_2 = 3.1415926; // 圆周率
printf("圆周率为:%.7f。\n", pi_2); // 输出浮点数的7位小数
return 0;
}
练习题
字符常量应是单引号。
操作数为字符或短整形时,系统自动转换成整形。操作数为实型时,系统自动转换成双精度型。当两数操作数类型不同时,将精度低(或表示范围小的)的操作数的数据类型变换到与另一操作数类型相同再进行运算。
题解同上一题;
4. 算数运算符与表达式
算术与赋值运算符
C++中的基本算术运算分为如下5种:加法和减法,乘法和除法以及求模
C++使用运算符(operator)来完成这些算术运算
算术运算符都需要用到两个数值来计算结果
这两个数值被称作操作数
操作数可以是常量,也可以是变量
int cookie;
cookie = 5 + 3;
Tips:代码中的5和3都是操作数,符号+是加法运算符。
5 + 3构成了一个可以计算结果的式子,我们称它为表达式
赋值运算符
C++中的基本算术运算分为如下5种:加法和减法,乘法和除法以及求模
C++中,"="被称作赋值运算符,代表把表达式的值赋给变量的操作
赋值的行为都是从右往左进行的
左边项必须引用一个存储位置,如一个变量名
Tips:赋值运算符与数学中的等于号含义不同:cookie = cookie - 1;在数学上显然不成立;但在计算机中,这个语句可以表示吃掉一块饼干后的结果。
C++中,5种基本算术运算符的计算规则与效果如下:
+运算符对两个操作数执行加法运算
-运算符从左边的操作数中减去右边操作数的数值
*运算符将两个操作数相乘
/运算符用左边的操作数中除以右边操作数
若两个操作数都是整数,则结果只返回商的整数部分
例如,表达式18 / 6,结果得到3;对于表达式19/6,结果也会得到3
若其中至少有一个浮点数,则小数部分将被保留
例如,表达式10 / 4.0,将得到2.5
%运算符进行求模运算,得到第一个操作数除以第二个操作数后的余数
执行此操作的两个操作数必须都是整数
例如,表达式32 % 5将会得到2,因为32是5的6倍再加上2
Tips:算术与赋值运算符在进行运算时,需要两个运算对象才能完成操作,它们被称为二元运算符。
优先级和结合律
曲奇饼干每盒有10块,家里还剩3盒,小键再买5盒以后,每天都会吃2块。
#include <iostream>
using namespace std;
int main() {
int days;
// TODO 计算购买之后,曲奇饼干够小键吃几天,赋值给变量 days
days = 10 * (3 + 5)/2;
cout << days << endl;
return 0;
}
自增自减运算符
C++中的++其实也是一种运算符,称为自增运算符
++运算符只能作用在变量操作数上,可以使该变量的值在原来基础上递增1
前缀模式:++出现在其作用的变量前面,例如:++coffee
后缀模式:++出现在其作用的变量后面,例如:milk++
输入:
int coffee_box;
int milk_box;
int coffee = 0;
int milk = 0;
coffee_box = ++coffee; //前缀模式
milk_box = milk++; //后缀模式
printf("coffee_box = %d, coffee = %d. \n", coffee_box, coffee);
printf("milk_box = %d, milk = %d. \n", milk_box, milk);
输出:
coffee_box = 1, coffee = 1.
milk_box = 0, milk = 1.
+运算符的两种模式都会将对应的变量数值增加1
变量coffee与milk的数值都由初始值0变为了1
前缀模式的变量将先于赋值运算符进行递增
coffee_box中的数值被赋值为递增之后的coffee变量值1
后缀模式时的变量将后于赋值运算符进行递增
milk_box中的数值被赋值为于递增之前的milk变量值0
Tips:自增运算符需要作用在一个确定的变量上,对于由变量构成的表达式应用++,例如:(coffee + milk)++会使得程序报错,因为计算机无法找到一个明确的位置去存储递增计算之后的结果。
C++中与自增运算对应的是自减运算,使用自减运算符--
--运算符同样作用在变量操作数上,可以使该变量的值在原来基础上递减1
前缀模式:--出现在其作用的变量前面,例如:--beer
后缀模式:--出现在其作用的变量后面,例如:wine--
Tips:前缀模式与后缀模式的区别在于递减运算的作用时间不同。
输入:
int beer_box;
int wine_box;
int beer = 1;
int wine = 1;
beer_box = --beer; //前缀模式
wine_box = wine--; //后缀模式
printf("beer_box = %d, beer = %d. \n", beer_box, beer);
printf("wine_box = %d, wine = %d. \n", wine_box, wine);
输出:
beer_box = 0, beer = 0.
wine_box = 1, wine = 0.
--运算符的两种模式都会将对应的变量数值减少1
变量beer与wine的数值都由初始值1变为了0
前缀模式的变量将先于赋值运算符进行递减
beer_box中的数值被赋值为递增之后的beer变量值0
后缀模式时的变量将后于赋值运算符进行递增
wine_box中的数值被赋值为于递增之前的wine变量值1
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
int coffee_box;
int milk_box;
int coffee = 0;
int milk = 0;
// TODO 咖啡增加1,再赋值给 coffee_box
coffee_box = ++coffee;
// TODO 牛奶赋值给 milk_box,再增加1
milk_box = milk++;
printf("coffee_box = %d, coffee = %d. \n", coffee_box, coffee);
printf("milk_box = %d, milk = %d. \n", milk_box, milk);
int beer_box;
int wine_box;
int beer = 1;
int wine = 1;
// TODO 啤酒减少1,再赋值给 beer_box
beer_box = --beer;
// TODO 白酒赋值给 wine_box,再减少1
wine_box = wine--;
printf("beer_box = %d, beer = %d. \n", beer_box, beer);
printf("wine_box = %d, wine = %d. \n", wine_box, wine);
return 0;
}
计算且赋值运算符
cake = cake + 3;
nut = nut - 12;
cheese = cheese * 3;
butter = butter / 2;
flour = flour % 3;
C++提供了将算术运算与赋值操作相结合的运算符,称为计算且赋值运算符:
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
int cake=10, nut=30, cheese=5, butter=4, flour=11;
// TODO 蛋糕加3块
cake += 3;
// TODO 坚果减12颗
nut -= 12;
// TODO 芝士变为之前的3倍
cheese *= 3;
// TODO 黄油减半
butter /= 2;
// TODO 面粉分为3斤每份打包,最后剩下多少没法打包
flour %= 3;
printf("cake = %d, nut = %d, cheese = %d, butter = %d, flour = %d. \n", cake, nut, cheese, butter, flour);
return 0;
}
练习
#include <iostream>
using namespace std;
int main() {
// 请补全代码,实现题目功能
int a, b;
cin >> a >> b;
cout << a / b;
return 0;
}
5. 位运算符与表达式
C++按位运算符
本节内容将介绍C++中按位进行的运算符,简称位运算。
在计算机中,数据以二进制的形式储存
每8位(bit)构成一个字节(byte)
每个位具有0或1两种状态中的一种
Tips:通过多个字节组合,内存以二进制形式存放数值。例如,
字符类型char的常量7大小为1个字节
存储为8位二进制数:0000 0111
整数类型int的常量7大小为4个字节
存储为32位二进制数:0000 0000 0000 0000 0000 0000 0000 0111
C++中共包含6种按位运算符:
按位与运算符&
按位或运算符|
异或运算符^
按位取反运算符~
左移运算符<<
右移运算符>>
Tips:按位运算符只能对字符整型以及数值整型数据类型的常量或变量使用,不能对浮点类型数据进行计算。
位与运算符
按位与运算符&对两个操作数按照二进制位进行与运算。
Tips:对于操作数a和b,下表演示了&运算符的工作方式:
Tips:&运算符只有在两个操作数的对应位同时为1时,才会得到结果1。
举例:以变量A = 60,B = 13作为计算样例:
变量按位与运算符的程序段
int A = 60; // 0011 1100
int B = 13; // 0000 1101
cout << "A & B = " << (A & B) << endl;
对A和B按位进行与运算后,可以得到:
A & B = 0000 1100
二进制数0000 1100的值为12,故打印结果可得:
A & B = 12
#include <iostream>
using namespace std;
int main() {
int A = 60; // 0011 1100
int B = 13; // 0000 1101
// TODO 输出A和B按位与的结果
cout << "A & B = " << (A & B) << endl;
return 0;
}
位或运算符
按位或运算符|对两个操作数按照二进制位进行或运算。
Tips:对于操作数a和b,下表演示了|运算符的工作方式:
Tips:或运算只要在两个操作数的对应位存在1时,就可以得到结果1。
举例:以变量A = 60,B = 13作为计算样例:
int A = 60; // 0011 1100
int B = 13; // 0000 1101
cout << "A | B = " << (A | B) << endl;
对A和B应用位或运算符进行计算,可以得到:
A | B = 0011 1101
二进制数0011 1101的值为61,故打印结果可得:
A | B = 61
#include <iostream>
using namespace std;
int main() {
int A = 60; // 0011 1100
int B = 13; // 0000 1101
// TODO 输出A和B按位或的结果
cout << "A | B = " << (A | B) << endl;
return 0;
}
位异或运算符
位异或运算符^对两个操作数按照二进制位进行异或运算。
Tips:对于操作数a和b,下面的真值表演示了^运算符的工作方式:
Tips:异或运算在两个操作数的对应位不相同时,会得到结果1;也可以看作是二进制下的无进位相加。
举例:以变量A = 60,B = 13作为计算样例:
int A = 60; // 0011 1100
int B = 13; // 0000 1101
cout << "A ^ B = " << (A ^ B) << endl;
对A和B应用异或运算符进行计算,可以得到:
A ^ B = 0011 0001
二进制数0011 0001的值为49,故打印结果可得:
A ^ B = 49
#include <iostream>
using namespace std;
int main() {
int A = 60; // 0011 1100
int B = 13; // 0000 1101
// TODO 输出A和B按位异或的结果
cout << "A ^ B = " << (A ^ B) << endl;
return 0;
}
位取反运算符
C++中,取反运算符~对单个二进制操作数按位进行取反运算。
Tips:二进制位取反规则是0变1,1变0。
int A = 60; // 0011 1100
cout << "~A = " << (~A) << endl;
对A应用取反运算符进行计算,可以得到:
~A = 1100 0011
在计算机中,所有的二进制数据都是用补码保存的。
正数的补码就是原码本身
正数变量A的原码为0011 1100,补码同样等于0011 1100。
负数的补码是在其原码的基础上,符号位不变,其余各位取反然后+1
负数变量~A的原码为1100 0011,补码等于1011 1100 + 1 = 1011 1101,值为-61,故~A = -61。
根据取反运算的规则,打印结果可得:
~A = -61
#include <iostream>
using namespace std;
int main() {
int A = 60; // 0011 1100
// TODO 输出A按位取反的结果
cout << "~A = " << (~A) << endl;
return 0;
}
左右移运算符
在C++中,左移运算符<<可以将一个操作数的各二进制位全部左移若干位。
左边多余的二进制位将被丢弃
右边不足的二进制位将被补0
int A = 60; // 0011 1100
cout << "A << 1 = " << (A << 1) << endl;
对A应用左移运算符进行1次位移,可以得到:
A << 1 = 0111 1000
二进制数0111 1000的值为120,故打印结果可得:
A << 1 = 120
在结果符号位不改变,并且没有超过表示范围的情况下,左移运算符移动了几位,相当于对原操作数进行了几次乘2运算。
在C++中,右移运算符>>可以将一个操作数的各二进制位全部右移若干位。
右侧多余的位将会被舍弃
左侧对于无符号数,会在左侧补0
左侧对于有符号数,会用符号位补齐:正数为0,负数为1
#include <iostream>
using namespace std;
int main() {
int A = 60; // 0011 1100
// TODO 输出A左移1位的结果
cout << "A << 1 = " << (A << 1) << endl;
// TODO 输出A右移2位的结果
cout << "A >> 2 = " << (A >> 2) << endl;
return 0;
}
练习
输入三个整数a,b,c,输出a与b或运算的结果与c与运算的值。
输入描述:
一行,三个整数,用空格分开
输出描述:
一行,运算结果
输入:
1 2 3
输出:
3
#include <iostream>
using namespace std;
int main() {
int a, b, c;
cin >> a >> b >> c;
cout << ((a | b) & c);
return 0;
}
6. 关系与逻辑表达式
关系表达式
关系运算符中,表达式结果的类型可以是字符或者数字,但不可以是字符串
关系表达式返回一个布尔值:成立为1(true)代表真,不成立为0(false)代表假
浮点数一般不会用 == 或者 != 来判断,因为可能产生表示误差。
比较两个浮点数是否相等,需要看两个数的差值是否小于一定的精度,比如:
对于浮点数a和b,如果(a - b) < 1e-6 如果值为真,就判断两个浮点数相等。
举例:假设血量是整型数,小六希望判断己方血量hp_a和敌方血量hp_b两个数值之间的关系。
int hp_a, hp_b;
hp_a = 456;
hp_b = 857;
cout << "己方血量: " << hp_a << " 敌方血量: " << hp_b << endl;
// 关系运算符两边都是变量的基础用法
// 判断 自己血量 是否大于 敌方血量
cout << (hp_a > hp_b) << endl;
// 判断 自己血量 是否大于等于 敌方血量
cout << (hp_a >= hp_b) << endl;
// 判断 自己血量 是否小于 敌方血量
cout << (hp_a < hp_b) << endl;
// 判断 自己血量 是否小于等于 敌方血量
cout << (hp_a <= hp_b) << endl;
// 判断 自己血量 是否等于 敌方血量
cout << (hp_a == hp_b) << endl;
// 判断 自己血量 是否不等于 敌方血量
cout << (hp_a != hp_b) << endl;
// 关系运算符一边是表达式,一边是变量
// 判断 自己回50点血后的血量 是否小于 敌方血量
cout << ((hp_a + 50) < hp_b) << endl;
// 关系运算符一边是变量,一边是数字
// 判断 自己血量 是否小于 100
cout << (hp_a < 100) << endl;
举例:假设血量是浮点数,小六希望判断己方血量 hp_c 和 敌方血量hp_d 是否相等。
判断己方与敌方血量(浮点数)关系的程序段
float hp_c, hp_d;
hp_c = 100.0;
hp_d = 100.0;
cout << ((hp_c - hp_d) < 1e-6) << endl;
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int hp_a, hp_b;
hp_a = 456;
hp_b = 857;
cout << "己方血量: " << hp_a << " 敌方血量: " << hp_b << endl;
// TODO 判断 自己血量 是否大于 敌方血量
cout << (hp_a > hp_b) << endl;
// TODO 判断 自己血量 是否大于等于 敌方血量
cout << (hp_a >= hp_b) << endl;
// TODO 判断 自己血量 是否小于 敌方血量
cout << (hp_a < hp_b) << endl;
// TODO 判断 自己血量 是否小于等于 敌方血量
cout << (hp_a <= hp_b) << endl;
// TODO 判断 自己血量 是否等于 敌方血量
cout << (hp_a == hp_b) << endl;
// TODO 判断 自己血量 是否不等于 敌方血量
cout << (hp_a != hp_b) << endl;
// TODO 判断 自己回50点血后的血量 是否小于 敌方血量
cout << ((hp_a + 50) < hp_b) << endl;
// TODO 判断 自己血量 是否小于 100
cout << (hp_a < 100) << endl;
float hp_c, hp_d;
hp_c = 100.0;
hp_d = 100.0;
// TODO 判断 两个浮点数血量是否相等
cout << ((hp_c - hp_d) < 1e-6) << endl;
return 0;
}
逻辑表达式
关系表达式可以判断某个条件是否成立,逻辑表达式中包含了多个关系表达式,可以对多个条件进行判断,例如:
多个条件是否同时成立
多个条件中是否至少有一个成立
某个或某些条件是否不成立
C++使用逻辑运算符连接多个关系表达式,从而构建逻辑表达式。
常用的逻辑运算符有以下三种:
逻辑AND运算符&&:判断两个关系表达式是否同时成立
逻辑OR运算符||:判断两个关系表达式是否至少有一个成立
逻辑NOT运算符!:判断某个关系表达式是否不成立
假设p1和p2是关系表达式的返回结果,逻辑运算符的运算规则可以参考下表:
Tips:判断多个条件是否成立时,我们可以组合逻辑运算符,构成更复杂的逻辑表达式。
举例:判断当变量 a 是否是30以下或者100以上的偶数时,可以通过 (a%2==0) && ((a < 30) || (a > 100)) 得到结果。
举例:小六最近在游戏上进步了一点点,他知道有的时候撤不撤退,不只需要看自己血量是否比敌方高,还需要看自己的蓝量是否比敌方高。
假设布尔值 higher_hp 表示自己血量比对方高,higher_mp 表示自己蓝量比对方高。
小六想要知道:“是否自己血量和蓝量都比敌方高”,“是否自己血量和蓝量和中至少有一种比敌方高”,“是否血量没有敌方高”,“是否自己血量和蓝量都没有敌方高”,你能够把这些信息都输出给小六吗?
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
// 假设小六血量比敌方高,但是蓝量没有敌方高
bool higher_hp = 1;
bool higher_mp = 0;
// TODO 输出是否自己血量和蓝量都比敌方高
cout << (higher_hp && higher_mp) << endl;
// TODO 输出是否自己血量和蓝量和中至少有一种比敌方高
cout << (higher_hp || higher_mp) << endl;
// TODO 输出是否血量没有敌方高
cout << (!higher_hp) << endl;
// TODO 输出是否自己血量和蓝量都没有敌方高
cout << (!higher_hp && !higher_mp) << endl;
return 0;
}
运算符优先级
至此,我们已经学习的运算符如下:
算术运算符(+ - * / % ++ --等)
赋值运算符(=等)
位运算符(<< >>等)
关系运算符(> >= < <= == !=等)
逻辑运算符(&& || !等)
对于这些常用的运算符,按照计算优先级从高到低(左边更高,右边更低)进行排列,可以得到如下表格:
Tips:对于一个表达式,虽然可以通过计算优先级省略掉括号,但为了代码的可读性,我们在编写时一般都选择把括号加上。
代码:判断闰年
举例:生日在2月29日的程序员小键希望用一段代码判断输入的某一年是否为闰年。
判断“闰年”的口诀是:四年一闰,百年不闰,四百年又闰。即闰年分为两种:
一种是被400整除的年份
一种是不被100整除,但是被4整除的年份
int year;
cin >> year;
bool is_leap_year;
// 是400的倍数;或者是4的倍数并且不是100的倍数
is_leap_year = (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
cout << is_leap_year << endl;
在C++中,对于逻辑运算符&&、||、!来说:
参与计算的条件返回值为0,就会被当作假
如果year = 2000是400的倍数,year % 400的结果为0,此时(year % 400)会被当作假
参与计算的条件返回值非0,就会被当作真
如果year = 2001不是400的倍数,year % 400结果为1,此时(year % 400)会被当作真
通过这个特性,用逻辑表达式判断闰年也可以这么写:
int year;
cin >> year;
bool is_leap_year;
is_leap_year = !(year % 400) || (!(year % 4) && (year % 100));
cout << is_leap_year << endl;
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int year;
cin >> year;
bool is_leap_year;
// TODO 请用一个表达式来判断 year 是否为闰年
is_leap_year = !(year % 400) || (!(year % 4) && (year % 100));
cout << is_leap_year << endl;
return 0;
}
练习
绝对值
输入一个整数,输出该数的绝对值。
额外提示: if语句的写法
if ( 表达式 ) {
// 如果表达式为真将执行的语句
}
else {
// 如果表达式为假将执行的语句(可以没有else分支)
}
输入描述:
一行,一个整数
输出描述:
一行,该数的绝对值
输入:
-10
输出:
10
#include <iostream>
using namespace std;
int main() {
int a;
cin >> a;
if (a >= 0) {
cout << a;
}
else {
cout << -a;
}
return 0;
}
倍数或判定
输入一个数字,判断它是否是5的倍数,或者是7的倍数,满足任一条件则输出yes,否则输出no
额外提示:if语句的写法
if ( 表达式 ) {
// 如果表达式为真将执行的语句
}
else {
// 如果表达式为假将执行的语句(可以没有else分支)
}
输入描述:
一行,一个整数
输出描述:
一行,yes 或 no
#include <iostream>
using namespace std;
int main() {
int a;
cin >> a;
if (a % 5 == 0 || a % 7 == 0){
cout << "yes";
}
else {
cout << "no";
}
return 0;
}
是字母吗
输入一个字符,若是大写或小写字母,输出yes,否则输出no
额外提示:if语句的写法
if ( 表达式 ) {
// 如果表达式为真将执行的语句
}
else {
// 如果表达式为假将执行的语句(可以没有else分支)
}
输入描述:
一行,一个字符
输出描述:
一行,yes 或 no
#include <iostream>
using namespace std;
int main() {
char a;
cin >> a;
if (('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z')){
cout << "yes";
}
else {
cout << "no";
}
return 0;
}
7. if语句
if 基础用法
if 基础用法:控制程序在指定条件下执行指定语句。
流程如下图:在满足某个条件时,执行语句-做事件A;不满足条件时,直接跳过做事的模块。
if (成立条件表达式) {
当条件成立时需要执行的语句
}
语法解析:
成立条件表达式:用圆括号包裹,一般是关系表达式或者逻辑表达式,比如:a<b, a<b && b<c
如果条件成立(成立条件表达式的值为“1或True”),则执行语句;
执行语句可以是一行代码,也可以是多行代码
如果执行语句是多行代码,需要用大括号包裹;
执行语句相比其他语句向右移动一些,就是缩进。读者可以一目了然的知道哪些语句是当条件成立时执行的,可读性更强。
// 输入一个double类型的变量表示降水概率
double prob_of_precipitation;
cin >> prob_of_precipitation;
// 在降水概率大于40%这个条件成立时,需要执行的语句是:输出“请带上伞”。
if (prob_of_precipitation > 0.4)
cout << "请带上伞" << endl;
如果降雨条件成立时,要首先输出降水概率,并且输出“请带上伞”,那么小键就需要在执行语句外加上大括号:
double prob_of_precipitation;
cin >> prob_of_precipitation;
if (prob_of_precipitation > 0.4) {
cout << "降水概率:" << prob_of_precipitation << endl;
cout << "请带上伞" << endl;
}
#include <iostream>
using namespace std;
int main() {
double prob_of_precipitation;
cin >> prob_of_precipitation;
// TODO 降水概率大于40%时,输出降水概率以及“请带上伞”。
if (prob_of_precipitation > 0.4){
cout << "降水概率: " << prob_of_precipitation << endl;
cout << "请带上伞" << endl;
}
return 0;
}
if ... else 用法
if ... else 用法:用来控制程序在指定条件下做事情A,否则做另外去做事情B。
if (成立条件表达式) {
当条件成立时需要执行的语句
} else {
当条件不成立时需要执行的语句
}
// 先输入一个整型表示年龄
int age;
cin >> age;
// 判断年龄是否大于等于18,成立的话就输出“允许进入”;不成立就输出“禁止进入”。
if (age >= 18)
cout << "允许进入" << endl;
else
cout << "禁止进入" << endl;
#include <iostream>
using namespace std;
int main() {
int age;
cin >> age;
// TODO 判断年龄是否大于等于18,成立的话就输出“允许进入”;不成立就输出“禁止进入”。
if (age >= 18)
cout << "允许进入" << endl;
else
cout << "禁止进入" << endl;
return 0;
}
if... else if 用法
if ... else if 用法:有些时候,我们会遇到大于两个分支的复杂情况。这时候,我们需要用到 if ... else if 语句,保证在多种情况下,不同条件成立时可以做不同的事情(如下图):
if (成立条件表达式1) {
当条件1成立时需要执行的语句
} else if (成立条件表达式2) {
条件1不成立但是条件2成立时需要执行的语句
} else {
当条件1和2都不成立时需要执行的语句
}
语法执行流程:
在条件1成立时进行一些操作;
否则看条件2是否成立;
如果条件2成立,做一些操作;
否则做条件1和2都不成立时需要的操作。
注意点:
成立条件表达式:关系或者逻辑表达式;
最后的一个else:可以没有,你可以选择在条件1和条件2都不成立时,不做任何事情。
#include <iostream>
using namespace std;
int main() {
int age;
cin >> age;
// TODO 对于大于等于60岁的老人-输出"免票"
// 对于小于12岁的小孩-输出"半票"
// 对于其他人-输出"全票"
if (age > 60)
cout << "免票" << endl;
else if (age < 12)
cout << "半价" << endl;
else
cout << "全票" << endl;
return 0;
}
if... else if 的延伸用法
if ... else if 延伸用法:如果情况需要分成不只三种,我们也可以在else后面再接if、else、if、else,依次判断每个条件是否成立,成立就执行对应的操作,否则就判断下一个条件是否成立:
if (成立条件表达式1) {
当条件1成立时需要执行的语句
} else if (成立条件表达式2) {
否则,当条件2成立时需要执行的语句
} else if (成立条件表达式3) {
否则,当条件3成立时需要执行的语句
} ... else if (成立条件表达式n) {
否则,当条件n成立时需要执行的语句
} else {
当上述所有条件都不成立时需要执行的语句
}
#include <iostream>
using namespace std;
int main() {
int age;
cin >> age;
// TODO 年龄小于5岁,输出“免票”;
// 年龄在5-12岁之间(包含5岁和12岁),输出“7折票”;
// 年龄大于等于60岁,输出“5折票”;
// 否则输出“全票”
if (age < 5)
cout << "免票" << endl;
else if (5 <= age <= 12)
cout << "7折票" << endl;
else if (age > 60)
cout << "5折票" << endl;
else
cout << "全票" << endl;
return 0;
}
未完待续 2022.04.16
--------------------------------------------------------------------------------------------------------------------------------