• C++第5章-循环和关系表达式


    第5章-循环和关系表达式

    主要知识点:递增/递减运算符。

    5.1 for循环

    cout.setf(ios_base::boolalpha):通常,cout在显示bool值之前都会转换成int,此函数设置了一个标记,命令cout显示true或false,而非1或0。

    递增/递减运算符

    分为前缀(prefix)++i,后缀(postfix)i++

    • 前缀函数:先加后用。将值i加1,然后把i+1赋给i
    • 后缀函数:先用再加。把i复制一个副本,将副本的值加1,然后把副本的值i+1赋给i

    从上面也可以看出,前缀的速度要比后缀高。后缀是比较难理解的,我们结合例子去解释:

    int a = 20;
    int b = 20;
    cout << a << " " << b << endl;	// 20 20
    cout << a++ << " " << ++b << endl;	// 20 21
    cout << a << " " << b << endl;	//21 21
    

    第4行其实可以先int c = a++;,便可以写成cout << c ...,也就是输出的是c的值,那么为什么c是20而不是21呢?
    首先从运算的优先级上讲,递增运算高于赋值操作,所以a先复制一个副本并+1,但是什么时候把这个副本值赋给a呢?执行完顺序点!接下来引入副作用顺序点

    • 副作用(side effect):计算表达式时对某些东西,比如存在变量中的值进行了修改;
    • 顺序点(sequence point):程序执行过程中的一个点,在进入下一步之前要确保对所有的副作用进行了评估。C++中,分号就是一个顺序点,意味着程序处理下一条语句前,赋值运算符和递增/递减运算符的所有修改必须完成

    所以对于int c = a++;来说,后缀操作只能执行到副本+1这一步,之后就要执行把值a赋给c这步,而此时a的值仍是20,所以c的值也是20。

    程序在执行完第4行后,要对所有的副作用进行评估,便会把副本+1的结果赋给a,执行完第4行后,a的值更新为了21。也就是说:顺序点执行完成后,后缀运算才会把副本+1的值赋给本身

    ++b就没有上面那么麻烦了,执行完后就会直接更新b的值。

    对于for循环的自增操作,比如for(int i=0; i<10; i++),我们可以看到括号里是用的分号隔开,这意味着每个表达式的末尾是个顺序点,所以执行完i++后,就完成了把副本+1的值赋给i的这个过程。

    递增/递减和while循环

    int guests = 0;
    while(guests++ < 10)
    	cout << guests << endl;
    
    // output: 1,2,3...9,10
    

    首先guests++ < 10的末尾是个顺序点,当guests=0是,执行完这个顺序点guests=1,传给下面的cout打印;直到guests=9,执行完顺序点后guests=10,此时便不符合guests++ < 10这个条件了,程序结束。

    递增/递减和指针

    这里主要涉及优先级问题,

    • 前缀递增/递减和*运算符的优先级相同,故整体上从右向左结合;
    • 后缀递增/递减优先级高于前缀,自然也高于*

    pt->arr[0],可以总结如下:

    常见类型 解释
    *++pt; 先运算++pt,则pt->arr[1],然后取arr[1]的值
    ++*pt; 先取*ptarr[0]的值,然后++arr[0]
    (*pt)++; 括号优先级最高,所以先取*ptarr[0]的值,然后arr[0]++
    *pt++; 先执行pt++,结合上文此时pt->arr[0],然后取arr[0]的值,这句执行完后(顺序点)才有pt->arr[1]

    语句块和变量

    如果在一个语句块内声明一个变量,而外部语句块中也有一个这种变量,如下面所示:

    int x = 10;
    {
    	cout << x << endl;	// 10
    	int x = 100;
    	cout << x << endl;	// 100
    }
    cout << x << endl;		// 10
    

    从第4行声明新变量开始,到这个语句块结束,新变量将隐藏旧变量。结束后该变量再次可见。

    逗号运算符和字符串翻转

    #include <iostream>
    int main() {
    	using namespace std;
    	cout << "Enter a word: " << endl;
    	string word;
    	cin >> word;
    
    	char temp;
    	int i, j;
    	for (j=0, i=word.size()-1; j<i; --i, ++j) {
    		temp = word[i];
    		word[i] = word[j];
    		word[j] = temp;
    	}
    	cout << word << "
    Done!";
    	return 0;
    }
    

    执行效果:输入stressed,输出desserts。这个代码在string类有更好的实现方式,但现阶段可以先参考这个使用。

    第10行中使用逗号同时操作ij,这是逗号运算符。允许把两条或更多语句放在C++语法只允许放一个表达式的地方。第10行的本质是把这两个合成为1个。

    也可以这样写int j=0, i=word.size()-1;也能取得同样的效果,但这里的,就是列表分隔符,而不是逗号运算符。同样的还有第9行中,int i, j同时初始化ij,也是分隔列表中的变量。

    逗号运算符有如下三个特点:

    • 逗号运算符是个顺序点。先确保第一个表达式,然后计算第二个表达式。i=20, j=2*i则有i=20, j=40
    • 逗号表达式的值是第二部分。比如上面的表达式的值就是40,转换成bool类型为true;
    • 逗号运算符的优先级是最低的。(cat=70), 240执行括号中的表达式,240没什么作用。

    cat=(70, 240):这个表达式结合上面的第二点,cat=240

    关系运算符

    x+3 < y-2等价于(x+3) < (y-2)。这是因为关系运算符的优先级低于算术运算符

    字符串的比较

    我们可以使用stringchar[]来使用字符串。string在比较字符串时,比如string word;,我们可以word == "mate"来获得结果。
    但是如果是字符数组(我们也称为“C-风格字符串”),比如char word[20];word代表的是数组的地址,而字符串也是地址,所以word == "mate"的意思是判断这两个的地址是否相同,答案肯定是不相等。
    对于C-风格字符串,我们要使用srcmp(),已知str1和str2,则结果如下:

    str1和str2关系 strcmp(str1, str2)
    str1 = str2 0
    str1 != str2 非0
    str1在str2前面 <0
    str1在str2后面 >0

    有关这个函数的使用,下面这个例子是匹配相等的例子:

    char word[5] = "?ate";
    for (char ch='a'; strcmp(word, "mate"); ch++) {
    	cout << word << endl;
    	word[0] = ch;
    }
    cout << "After the loop, word is " << word << endl;
    

    由于strcmp的结果在不相等时不为0,不管其为正还是负,其bool类型都为true。

    因为C-风格字符串是通结尾处的空值字符()定义的,而不是所在的数组的长度定义的,所以即便两个字符串存储在长度不同的数组中,结果也有可能是相通的,比如下面这种情况也会被认为是相等:

    • char big[80]="Daffy";
    • char little[6]="Daffy";

    关系运算符不能用来比较字符串,但是可以用来比较字符,因为char型字符对应于ASCII字符集的值,比如下面这个例子:

    for (ch = 'a'; ch <= 'z'; ch++)
    	cout << ch;
    

    5.2 while循环

    for循环和while循环几乎是等效的,但一般在使用过程中,for循环来为循环计数,而在无法预知循环将要执行的次数时,一般使用while循环。

    延时循环

    为了让程序等待一段时间,可以这样写:

    long wait = 0;
    while (wait<10000)
    	wait++;
    

    但是这样的缺陷是不同平台的执行时间不同,更高性能的平台可能根本察觉不到等待,甚至有的编译器灰自动跳过这个循环。解决方法是使用系统时钟。
    函数clock()的返回结果是:程序开始执行后所用的系统时间,但是返回的时间的不一定是秒,且格式可能是long,也可能是unsigned long或其它类型。为了解决这个问题,需要使用头文件ctime
    其定义了一个CLOCKS_PER_SEC,该常量等于每秒钟包含的系统时间单位数,即用秒数乘以这个常数=以系统时间单位为单位的时间。其次,ctime规定了clock_t作为clock()返回类型的别名,所以不管返回的是long还是别的类型,我们只需要使用这个clock_t而不需要纠结到底是什么类型。
    使用举例:

    #include  <iostream>
    #include  <ctime>
    
    int  main() {
    	using  namespace  std;
    	float secs;
    	cin >> secs;
    	clock_t deleay = secs*CLOCKS_PER_SEC;
    	cout << "startinga
    ";
    	clock_t start = clock();
    	while(clock() - start < deleay)
    		;
    	cout << "done!" << endl;
    	return  0;
    }
    

    类型别名

    建立别名有两种方式:

    • 预处理器:#define BYTE char预处理器在编译程序时用char替换所有的BYTE;
    • typedef char byte 设置byte为char的别名,typedef char * byte_pointer为声明byte_pointer为char指针。

    这两方式的区别:

    #define FLOAT_POINTER float *
    FLOAT_POINTER pa, pb;
    

    编译器会转换成float * pa, pb;这样代表了pa是个指针,而pb只是个float类型。而使用typedef不会出现这样的问题。

    5.3 do-while循环

    5.4 基于范围的for循环

    写法如下:

    double  prices[5] = {4.4, 5.5, 3, 5, 6};
    for (double x : prices)
    	cout << x << endl;
    
    // 若要修改数组的元素,需要这样写;
    for (double &x : prices)
    	x = x*0.8;
    

    5.5 循环和文本输入

    使用cin进行输入控制

    char ch;
    int count = 0;
    cin >> ch;
    while(ch != '#') {	// '#'又被称为“哨兵字符”;
    	cout << ch;
    	++count;
    	cin >> ch;
    }
    cout << "
    Count number: " << count << endl;
    

    测试:

    cd Users#test
    cdUsers
    Count number: 7
    

    可见,输入中的空格在输出是被省略且没有被计算进计数中。这是因为cin会忽略空格和换行符。为了修改这个问题,我们可以使用cin.get(ch)

    char ch;
    int count = 0;
    cin.get(ch);
    while(ch != '#') {	// '#'又被称为“哨兵字符”;
    	cout << ch;
    	++count;
    	cin.get(ch);
    }
    cout << "
    Count number: " << count << endl;
    

    测试结果:

    cd Users#test
    cd Users
    Count number: 8
    
  • 相关阅读:
    IT痴汉的工作现状24-Just for fun
    &quot;duplicate symbol for architecture i386&quot; 解决的方法
    如何将visual studio 2010编辑模式改为插入???
    教学平台服务器安装环境说明
    Microsoft SQL Server 2008 基本安装说明
    WEB安全测试的类型
    IBM Rational Appscan使用之扫描结果分析
    IBM Rational AppScan使用详细说明
    Security Testing Basics
    ZAP介绍
  • 原文地址:https://www.cnblogs.com/rongyupan/p/14327988.html
Copyright © 2020-2023  润新知