C++ Primer Plus(嵌入式公开课)---第5,6章 循环和关系表达式 & 分支语句和逻辑运算符

20220222

C++ Primer Plus - 第五、六章

附录:

1.ASCII码字符对照表

点这里

2.C++ 运算符优先级

点这里

第1-3章

点这里

第4章 复合类型

点这里

第5章 循环和关系表达式

5.1 for循环

5.1.1 for循环的组成部分—表达式和语句的区别、输出true和false

①输出true和false;
②表达式age = 100 和 语句age = 100; 的区别;
在这里插入图片描述

5.1.2 回到for循环—const int SIZE = 10;为什么书上的程序敲出来会报错???


const int SIZE = 10;

代码:

#include<iostream>
using namespace std;

const int size = 10;

int main() {
   
//5.1.2
	int arr1[size];//size不明确
	for (int i = 0; i < size; i++) {
   //size不明确
		cout << i + 1 << " ";
	}

	system("pause");
	return 0;
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5.1.6 副作用和顺序点


while(guests++ < 10)
cout << guests << endl;

程序执行的顺序是:
先是guests < 10
然后guests++
最后cout << guests


y = (4 + x++) + (6 + x++);
C++没有规定是在计算每个子表达式之后将x的值递增,还是在整个表达式计算完毕后才将x的值递增,所以应避免使用上面的写法。

5.1.7 前缀格式和后缀格式—i++ 和 ++i 的效率(内置类型和用户自定义类型)


for(int i = 0; i < 10; i++)//后缀格式

for(int i = 0; i < 10; ++i)//前缀格式

对于内置类型来说,上面两种格式没有差别;
但对于用户自定义的类型,如果有用户定义的递增和递减运算符,则前缀格式的效率更高,原因见下面:
在这里插入图片描述
这篇博客解释的很清楚:C++ 递增运算符:前置++和后置++的区别
首先说下结论:迭代器和其他模板对象应该使用前缀形式 (++i) 的自增,,自减运算符,因为前置自增 (++i) 通常要比后置自增 (i++) 效率更高

前置++和后置++,有4点不同:返回类型不同、形参不同、代码不同、效率不同:


int main()     
{
        
    Age a;     
    
    (a++)++;  //编译错误     
    ++(a++);  //编译错误     
    a++ = 1;   //编译错误     
    (++a)++;  //OK     
    ++(++a);  //OK     
    ++a = 1;   //OK     
} 

  1. 返回类型不同
    前置++的返回类型是左值引用,后置++的返回类型const右值。而左值和右值,决定了前置++和后置++的用法。
    问:++a的返回类型为什么是引用呢?
    答:为了与内置类型的行为保持一致。前置++返回的总是被自增的对象本身。因此,++(++a)的效果就是a被自增两次。
  2. 形参的区别
    前置++没有形参,而后置++有一个int形参,但是该形参也没有被用到。很奇怪,难道有什么特殊的用意?
    其实也没有特殊的用意,只是为了绕过语法的限制
    前置++与后置++的操作符重载函数,函数原型必须不同,否则就违反了“重载函数必须拥有不同的函数原型”的语法规定。
    虽然前置++与后置++的返回类型不同,但是返回类型不属于函数原型。为了绕过语法限制,只好给后置++增加了一个int形参。
    原因就是这么简单,真的没其他特殊用意。其实,给前置++增加形参也可以;增加一个double形参只要不是int形参就可以。只是,当时就这么决定了。
  3. 代码实现的区别
    前置++的实现比较简单,自增之后,将*this返回即可。需要注意的是,一定要返回*this
    后置++的实现稍微麻烦一些。因为要返回自增之前的对象,所以先将对象拷贝一份,再进行自增,最后返回那个拷贝
  4. 效率的区别
    如果不需要返回自增之前的值,那么前置++和后置++的计算效果都一样。但是,我们仍然应该优先使用前置++,尤其是对于用户自定义类型的自增操作
    前置++的效率更高,理由是:后置++会生成临时对象

C++Primer中(P132)有这样简介的描述:
前置版本将对象本身作为左值返回,后置版本则将原始对象的副本作为右值返回,两种运算符必须作用于左值运算对象。后置版本需要拷贝副本,所以会影响程序的性能

C++笔记10:运算符重载中有为自定义数据类型重载++运算符的内容 ,具体代码就是上面的第3点 代码实现的区别所说的:
用类成员函数实现的

//重载++运算符:
	//前置++:
	MyInt& operator++() {
   //为什么要返回引用,是为了一直对一个数据(这里是自定义数据---类)进行递增操作
		num++;//先++
		return *this;//再返回对象本身this
		
	}

	//后置++:
	MyInt operator++(int) {
   //这里的int代表占位参数,可以用于区分前置和后置递增,
							//141和134两行属于重载,重载的条件是参数的个数、顺序、类型不同,
							//所以就加了个int,而且编译器只认int,其他的都不行,只有int可以区分开类++和++类
		MyInt temp = *this;//先记录对象本身this
		num++;//后++
		return temp;//最后将记录的返回,注意这里返回的是值,不是引用&
	}


5.1.8 递增/递减运算符和指针—优先级:后缀 > 前缀 == 解引用 pt++、++* pt 、*++pt、(*pt)++


//前缀递增、前缀递减、解引用运算符优先级相同;
//后缀递增、后缀递减优先级相同,且高于前缀和解引用运算符。
在这里插入图片描述

//5.1.8
	//前缀递增、前缀递减、解引用运算符优先级相同:
	//后缀递增、后缀递减优先级相同,且高于前缀和解引用运算符。
	
	double arr[5] = {
    11.1,22.2,33.3,44.4,55.5 };
	double* pt = arr;
	for (int i = 0; i < 5; i++) {
   
		cout << *(pt + i) << ", ";
	}
	cout << "\n" << endl;		//11.1, 22.2, 33.3, 44.4, 55.5

	cout << "*pt = " << *pt << "\n" << endl;//11.1

	//前缀递增 和 解引用运算符:
	cout << "*++pt = " << *++pt << endl;//22.2 相当于*(++pt)
	cout << "*pt = " << *pt << endl;//22.2
	cout << "++*pt = " << ++*pt << endl;//23.2 相当于++(*pt)
	cout << "*pt = " << *pt << endl;//23.2
	cout << endl;

	//后缀递增 和 解引用运算符:
	cout << "(*pt)++ = " << (*pt)++ << endl;//23.2 
	cout << "*pt = " << *pt << endl;//24.2
	cout << "*pt++ = " << *pt++ << endl;//24.2 相当于*(pt++)
	cout << "*pt = " << *pt << endl;//33.3
	cout << endl;

	//前缀递增、后缀递增、后缀递减、前缀递减:
	++pt;
	cout << "*pt = " << *pt << endl;//44.4
	pt++;
	cout << "*pt = " << *pt << endl;//55.5
	pt--;
	cout << "*pt = " << *pt << endl;//44.4
	--pt;
	cout << "*pt = " << *pt << endl;//33.3
	cout << endl;

	for (int i = 0; i < 5; i++) {
   
		cout << arr[i] << ", ";
	}
	cout << endl;					//11.1, 24.2, 33.3, 44.4, 55.5

结果:
在这里插入图片描述

5.1.10 复合语句(语句块)

在这里插入图片描述

5.1.11 其他语法技巧—逗号运算符

在这里插入图片描述

5.1.12 关系表达式—优先级:算术 > 关系运算符(>、<、=、>=、<=、!=、==)

①关系运算符的优先级比算术运算符低;
②关系表达式对两个值进行比较,常被用作循环测试条件。

在这里插入图片描述

5.1.13 赋值、比较和可能犯的错误—判断是否相等的时候用==,而不是=

在这里插入图片描述
对于第二种情况,书中的解释:由于它将20赋给数组元素quizscores[i],因此表达式始终为非零,所以始终为true。

也可以看看5.1.1.的②:表达式 和 语句

5.2 while循环

5.2.2 编写延时循环—#include< ctime>

#include<iostream>
#include<ctime>
using namespace std;

int main() {
   

	cout << "请输入您想要延迟的时长(单位:秒/s):";
	float sec;
	cin >> sec;
	clock_t delay = sec * CLOCKS_PER_SEC;//5*1000
	clock_t start = clock();
	while (clock() - start < delay) {
   
		//if ((clock() - start) > 0 && (clock() - start) % CLOCKS_PER_SEC == 0)//1000的整数倍:1000,2000,3000...
		//	cout << "第 " << (clock() - start) / CLOCKS_PER_SEC  << " 秒" << endl;
	}
	cout << "clock() = "<< clock()<<",共计时 " << (clock() - start) / CLOCKS_PER_SEC << " 秒。" << endl;

	system("pause");
	return 0;
}

5.2.2 类型别名(为已有类型建立一个新名称)—typedef 优于 #define

①#define(没分号
#define SIZE 100 //SIZE是100的别名
#define BYTE char //BYTE 是 char类型 的别名,预处理器将在编译程序时用char 替换所有的BYTE

注意:当需要声明一系列变量时,这种方法不适用:
在这里插入图片描述
②typedef (有分号
typedef char byte; //byte 是 char类型 的别名
typedef char* byte_pointer; //byte_pointer 是 char指针 的别名
typedef int word; //word 作为 int 的别名,那么cout将把word类型的值视为int类型

注意:
typedef不会创建新类型,而只是为已有的类型建立一个新名称。

5.3 do-while循环

do-while循环是出口条件(exit condition)循环:先执行循环体,然后再判定测试表达式,决定是否继续执行循环;
for循环和while循环是一种入口条件循环:如果测试条件为非零,则进入循环执行循环体;如果测试条件一开始就是false,则不会进入循环。

5.4 基于范围的for循环(C++11)

在这里插入图片描述

5.5 循环和文本输入

5.5.1 5.5.2 5.5.3 — 逐个字符char ch;输入

5.5.1 使用原始的cin进行输入
在这里插入图片描述
①直接用cin << ch;会忽略空格和换行符;
②为什么#后面还能输入字符?—见下图
在这里插入图片描述

5.5.2 使用cin.get(char)进行补救
①cin所属的istream类的一个成员函数cin.get(ch)可以读取输入的下一个字符(包括空格符),然后赋值给变量ch;
②#后面的内容依然会被缓冲,因此输入的字符个数仍可能比最终到达程序的要多。
在这里插入图片描述

思考:
在这里插入图片描述

//5.5.1 5.5.2
	char ch;
	int count = 0;
	//cin >> ch;//5.5.1
	cin.get(ch);//5.5.2
	while (ch != '#') {
   //#作为结束符
		cout << ch;
		count++;
		//cin >> ch;//5.5.1
		cin.get(ch);//5.5.2
	}
	cout << "共输入了 " << count << " 个字符。" << endl;

5.5.3 使用哪一个cin.get() — 函数重载

到目前见到了三种cin.get();

  1. cin.get();//没有参数
  2. cin.get(ch);//一个参数char类型
  3. cin.get(name,20);//两个参数:字符数组名,一次接收的字符数上限(③见第四章的 4.13 编程练习 的 第1题 )

这就是函数重载,函数重载允许创建多个同名函数,条件是他们的参数列表不同。

☆☆☆函数重载的应用:(1和2适合读单个字符,3适合读一整行字符串)

  1. char ch; ch = cin.get();//没有参数,返回值为输入中的下一个字符
  2. char ch; cin.get(ch);//一个参数char类型,参数是引用类型,直接将读到的字符赋给参数ch
  3. char name[20]; cin.get(name,20);//两个参数:字符数组名,一次接收的字符数上限(③见第四章的 4.13 编程练习 的 第1题 )

5.5.4 5.5.5 —判断是否到达文件尾EOF

5.5.4和5.5.5分别介绍了2种和1种方法,几种方法都可行,最终推荐5.5.4的程序2

5.5.4 cin.fail() == false 和 cin.eof() == false

二者都返回的是bool值:遇到EOF就返回true,否则范围false
cin.fail()cin.eof()多,因为前者可用于更多的实现中。


通过键盘模拟文件尾条件(EOF):Ctrl+ZEnter

示例:
在这里插入图片描述
程序1:

//5.5.4 5.5.5
	char ch;
	int count = 0;
	cin.get(ch);
	while (cin.fail() == false) {
   //判断是否到达文件尾 cin.eof() == false也可以
		cout << ch;
		count++;
		cin.get(ch);
	}
	cout << "共输入了 " << count << " 个字符。" << endl;

程序语句进一步精简:
在这里插入图片描述
具体的解释:
在这里插入图片描述
结论:
所以说,判断是否到达文件尾,可以直接用while(cin),这比cin.fail() == false)cin.eof() == false更通用,因为它可以检测到其他失败原因,如磁盘故障。

程序2:

//程序2:
	char ch;
	int count = 0;
	while (cin.get(ch)) {
   //判断是否到达文件尾 
		
		cout << ch;
		count++;
	}
	cout << "共输入了 " << count << " 个字符。" << endl;

在这里插入图片描述

5.5.5 (ch = cin.get()) != EOF

在这里插入图片描述
程序3:

//程序3:
	int ch;
	int count = 0;
	while ((ch = cin.get()) != EOF) {
   
		cout.put(char(ch));
		count++;
	}
	cout << "共输入了 " << count << " 个字符。" << endl;

程序3和程序1、2的区别在于:①ch是int类型;②循环条件;③输出的语句
为什么是要用int ch;来接收cin.get()的返回值?
在这里插入图片描述

分析一下循环条件(ch = cin.get()) != EOF
在这里插入图片描述

最后比较下5.5.4和5.5.5的差别:
在这里插入图片描述

5.6 嵌套循环和二维数组

C++没有提供二维数组类型,但用户可以创建每个元素本身就是数组的数组。

区分 char数组/字符数组、字符串指针数组、string对象数组 ☆☆☆

//5.6
	//字符串指针数组:
	const char* cities[5] = {
    "beijing","tianjin","xi'an","nanjing","xiamen" };
	
	//字符数组:
	char cities2[5][20]{
    
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值