Bootstrap

《C++ Primer Plus》阅读笔记(第5章)

笔记不定期更新,读到哪里更到哪里:D

5.1 for循环
使用方法:
int i;//举例
for(i=0;i<5;i++)    //控制部分,注意用分号分隔每个部分,结尾不需要分号
    cout<<"哼哼啊啊啊啊啊";        //循环体注意缩进

格式:
    for(初始化,循环测试,循环更新)
    循环测试部分的参数会被强制转换为bool类型,非0转为true,0转为false(如果通过这种方式转换为ture将会导致死循环)
    也就是说,循环测试部分的参数就像汇编语言中的cmp和je,先比较,比较完得出参数(个人理解,这部分可以划掉)。
    
    循环将在循环测试部分变为0后停止。

1.表达式和语句
    表达式的概念:任何值或任何有效的值和运算符的组合都是表达式
    在C++中,每个表达式都有值(由此可判断是不是表达式),如:
        22+22 是值为44的表达式。

    像x<y这样的关系表达式将被判定为bool值,如:
        int x;
        x=100;
        cout<<(x<3);    //结果是0,因为cout在显示bool值前会将其转换为int类型

    为了判定:表达式x=100,C++将会把100赋给x,
    当判定一个表达式的值这种操作改变了内存中的数据的值时,我们说表达式有副作用。
    我们在使用这种表达式时把赋值当成主要目的,但从C++的角度来看,判定才是主要目的。

    判定x+15并没有副作用,因为它将计算出一个新的值,因此,一般有副作用的表达式都是赋值表达式。

    将表达式转换成语句的方式:加上分号
    如:
          x=10        //是一个表达式
          x=10;    //是一个语句(表达式语句)

    编译器允许无意义的语句,但可能会直接跳过执行。

2.非表达式和语句
    任何表达式加上分号都能成为语句,但语句去掉分号不一定是表达式,如:
        int toad;         //它没有值,因此去掉分号也不是表达式
    也并非所有语句都满足语句=表达式+分号,如for语句。
        
    不是表达式就不能赋值和被赋值(因为不是表达式就没有值)

3.修改规则:
    可以这么做:
        for(int i=0;i<5;i++)     
    不要被书中的格式误导,不可以直接省略分号!!!(编程得来的经验)

    在循环初始化部分声明的变量在循环结束后就会消失(但某些老式实现会保留变量)

5.1.2 
    阶乘的方法:
           x*(x-1)!    //零阶乘(0!)被定义为1
    
     程序5.4:
    由于前两个阶乘的结果都是1,所以在循环开始前直接为数组进行赋值。

    表达式i<ArSize导致循环在数组索引到ArSize-1的地方停止

5.1.3
    可以通过修改循环更新表达式来改变步长。
    如:    
        int by=2;
        for(int i=0,i<5,i=i+by)    //注意赋值表达式的使用,i+by是无意义的


重点:循环更新表达式可以是任何有效的表达式

5.1.4
程序5.6:
    循环初始化中int i=word.size() - 1   //这里的字符串是使用cin得来的,结尾包含空字符,所以最后-1
    该程序使用了关系运算符大于或等于(>=)

5.1.5
    粗略地讲,a++意味着使用a的当前值进行计算,然后再将a的值+1
    ++a则是先把a+1,再计算。

    注意:不要在同一个语句内对同一个值递增或递减多次

5.1.6
    副作用指的是在计算表达式时对某些东西进行了修改,
    顺序点是程序执行过程中的一个点,在这里确保所有副作用都已完成。

    语句中的每个分号都是顺序点,任何完整的表达式末尾也是顺序点。
    
    y=(4+x++)+(6+x++);//分号是顺序点,因此这条语句在结束时才完成修改

5.1.7
    如果递增运算符仅进行计算,没有被使用,则使用前缀格式和后缀格式没有任何区别,
    但在用户定义的类中,前缀版本的效率比后缀版本的高。

5.1.8
    将*和++同时用于指针时:
    前缀递增递减和解除引用运算符的优先级相同,从右到左结合。
    后缀递增递减的优先级比前缀递增递减高,从左到右结合。

5.1.9 组合赋值运算符
    格式:
    +=、-=、*=、/=、%=
    含义:先将两个操作数进行计算,然后将得到的结果赋给左边的操作数

5.1.10
    可以在循环体内包含任意条语句,方法是用花括号构造一个代码块(代码块被视为一条语句)

    如果在代码块中声明变量,在代码块执行完毕后会释放该变量。

    如果内外部语句块中有两个名称相同的变量,则在内部语句块结束之前,新变量将代替旧变量。

5.1.11 逗号运算符
    逗号运算符允许两个表达式放到只允许放一个表达式的地方。

    在同一语句内,逗号只能有一种含义(列表分隔符或运算符),例如:
    for(int x=0,y=10; ......)不被允许,因为逗号已经是列表分割符了。

    逗号运算符还有另外两个特性:
        1.逗号运算符是一个顺序点
        2.逗号表达式的值是逗号右边的值

    在所有运算符中,逗号运算符的优先级是最低的,例如:
        x=7,240; //结果为7,240不起作用
    但这样的语句:
        x=(7,240);      //结果为240,原因为特性2

5.1.12 关系表达式
    关系运算符可以对数字进行比较,for语句中就是靠关系运算符得出ture和false,
    可以将它们用于:
        数字、字符、string类型。不能用于C风格字符串。

5.1.14
    因为C++将C风格字符串视为第一个字符的地址,因此无法使用==进行比较
    如果想要比较C风格字符串,需要使用strcmp()函数,该函数接受两个字符串地址作为函数,
    如果相同则返回0,如果第一个字符串的ASCII码比第二个字符串小,则返回负数,大则返回正数。
    (ASCII码中,大写字母的编码比小写字母小)

    这里顺便提一下之前学过的知识:C风格字符串的结尾是通过空字符而非数组长度确定的。

    可以使用strcmp()函数在字符串相等时返回0(false)的特性用它来进行循环比较。

    strcmp()的用法和汇编语言cmp有点像,如:
        int str1=1,str2=0;
        strcmp(str1,str2)>0;    //将返回true

5.1.15
    可以将关系运算符(如!=)用于string对象。例如:
        word !="mate"
    同时,可以使用数组表示法表示string对象的内容(老知识)

5.2 while循环
    while是只有测试条件和循环体的for循环。
    
        while(word[i] !='\0')
    它的循环体必须修改i的值,否则将陷入死循环。
    可以改成这样:
        while(word[i])
    它的效果不变,因为当i为空字符时,其编码为0,也就是false。
        
重点:不同于C风格字符串,string类不使用空字符来标记字符串末尾。

5.2.1
    省略for循环的测试表达式将会导致死循环。

    通常,程序员使用for循环,因为它能将所有相关的信息放到一个地方,
    而选择在无法预先知道循环的次数时使用while循环。

    记住,不要往循环的括号后面加分号,因为分号用来表示语句结束。

5.2.2
    为了实现延时,可以使用循环,但是更好的方式是使用系统时间完成这个任务。

    函数clock()可以完成这个任务,它返回开始执行后的系统时间,
    为了控制其返回的时间单位和类型,需要使用头文件<ctime>中的定义。
        它定义了一个符号常量:CLOCKS_PER_SEC,该常量为每秒钟包含的系统时间数,
        将系统时间除以它将得到秒数,将秒数乘以它也能得到系统时间。

        在ctime中定义了clock的返回类型(系统时间的类型)的别名clock_t,
        使用clock_t创建变量将会被转换为头文件定义的系统时间的类型。
        
        clock_t 变量名=输入的秒数*CLOCKS_PER_SEC;//可以将输入的秒数转换成系统时间
        
    程序5.14解读:
        clock_t delay =secs * CLOCKS+PER_SEC;//同上
        clock_t start=clock();    //获得循环开始前的时间
        while(clock() - start<delay);   //用循环开始后的时间减去开始前的时间,算出已经循环了多久

    类型别名:
        C++为类型建立别名的方式有两种:
            第一种:使用预处理器:#define 替换类型名 被替换的类型名
            第二种:使用关键字:typedef 被替换的类型名 替换类型名
            第二种方式同样可用于某种类型的指针,如:
                typedef char * byte_pointer;  //用byte_pointer代替char类型指针

            使用tapedef进行置换可以在声明指针变量时无视必须在每个指针前都加上*的规则,如:
                typedef type * char;
                type pa,pb;

5.3 do while循环
    do while循环不同于前面的两种循环是入口条件循环,它是出口条件循环,两者的差别是:
    入口条件循环有可能一次都不会执行循环体内语句,出口条件循环则至少会执行一次循环体内语句。

    do while循环多用于比较用户的输入。

5.4 基于范围的for循环(C++11)
    这种循环简化了对数组或容器(模板)类反复执行同样的操作的循环,如下所示:

        double prices[5]={4.99,10.99,6,87,7,99,8,49};
        for(double x : prices)     //冒号后面应为元素(使用{})或可代表元素的对象名(如数组名和指针)
            cout<<x;         //在这里,x代表prices的第一个元素

    如果要用这种循环修改数组的内容,则需要使用引用变量(第8章再谈具体的):
        for(double &x : prices)

5.5 循环和文本输入

5.5.1 cin进行输入
    cin将会忽略换行符和空格,而且更为复杂的是,发送给cin的输入将会被缓冲,
    也就是说,只有用户按下回车后,内容才会被发给程序。
    (这个地方建议自己去编程理解)

5.5.2
    这个地方就不多说了,使用cin.get()虽然能读入空格,但是输入依旧会被缓冲。
    引用是C++新增的一种类型,头文件iostream将cin.get(ch)的参数声明为引用类型(这段不重要)。

5.5.3
    C++中通过函数重载,可以声明多参数列表不同的同名函数(不同的函数版本)。
    剩下的依旧是在讲C++和C语言的不同之处,就不多说了。
    
5.5.4 文件尾条件
    前面的程序表明,使用某个符号表示输入结束很难令人满意(因为输入将被缓冲)
    如果输入来自文件,则可以使用一种技术:检测文件尾(EOF)

    很多操作系统都允许用文件替换键盘输入(重定向),
    例如有一个名为    gofish.exe的可执行程序和一个名为fishtale的文本文件,则可以:
        gofish<fishtale        //在命令提示符中输入这行命令,<是命令提示符模式的重定向运算符

重点:有些C++实现允许通过键盘来模拟文件尾条件,大多为使用Ctrl+Z(我使用的是Microsoft Visual)
    因此应在行首按下Ctrl+Z随后按下回车键。

    检测到EOF后,cin将两位(eobit和failbit)都设置为1,因此有两种方法可检测EOF是否被检测到:
        如果检测到EOF,则函数cin.eof()返回ture,否则将返回false    
        如果eobit或failbit位被设置为1,则函数cin.fail()返回ture,否则返回false

    注意,cin.eof和cin.fail在事后报告,因此应在读取后再使用它们。

    fail()相较于eof()可用于更多的实现中,因此程序5.18中使用了fail(),
    它使用cin.fail()检测EOF是否被检测到,如果结果为ture则循环检测部分为false(0),循环体不执行。

;