第三章 处理数据

3.1 简单变量

int braincount;
braincount = 5;

(1)以上声明语句和赋值语句告诉程序,它正在存储整数,并使用braincount表示该整数的值;
         实际上,程序找到一块能够存储整数的内存,将该内存标记为braincount,
         并将5复制到该内存单元中;然后可以在程序中使用braincount来访问该内存单元
(2)声明中使用的类型,描述了信息的类型,并通过符号来表示其值的变量名

3.1.1 变量名
(1)C++提倡使用一定含义的变量名,必须遵循以下命名规则
             ·名称中只能使用字母字符、数字和下划线(_)
             ·名称中的第一个字符不能是数字
             ·区分大写字符与小写字符
             ·不能将C++关键字用作名称
             ·以两个下划线或下划线和大写字母打头的名称保留给“实现”(编译器及其使用的资源)使用
             ·一个划线开头的名称被留给实现,用作全局标识符
            ·C++对于名称的长度没有限制,名称中的所有字符都有意义,但有些平台有长度限制
 
3.1.2 整型
(1)整型就是没有小数部分的数字
(2)C++的基本整型(按宽度递增顺序排列):char、short、int、long和long long
         其中每种类型都有有符号版本和无符号版本
(3)char类型常用来表示字符,而不是数字3.1.3 整型short、int、long和long long
(1)计算机内存由“位(bit)”单元组成,
         short、int、long和long long通过使用不同数目的位来存储值
(2)C++确保了它们的最小长度:
            ·short至少16位;
            ·int至少与short一样长;
             ·long至少32位,且至少与int一样长;
             ·long long至少64位,且至少与long一样长
         可以像使用int一样,使用这些类型名来声明变量
             short score;                              //生成一个short类型的变量
             int temperature;                          //生成一个int类型的变量
             long position;                            //生成一个long类型的变量
         这四种类型都是符号类型,所以每种类型的取值范围中,负值和正值范围几乎相同
            例:16位的int的取值范围为-32768到32767
(3)要知道系统中整数的最大长度,可以使用程序中的C++工具来检查类型的长度。
        sizeof运算符返回类型或变量的长度,单位为字节,字节的含义依赖于“实现”。
         头文件climits中包含了关于整型限制的信息(定义了各种限制符号的名称)
         例: INT_MAX为int的最大取值,CHAR_BIT为字节的位数

//以下程序演示了如何使用这些工具,以及如何初始化(使用声明语句将值赋给变量)
// limits.cpp -- some integer limits
#include <iostream>
#include <climits>              // use limits.h for older systems
int main()
{
    using namespace std;
    int n_int = INT_MAX;        // 初始化,声明了int型变量n_int,并将INT_MAX赋值给它
    short n_short = SHRT_MAX;   // symbols defined in climits file
    long n_long = LONG_MAX;
    long long n_llong = LLONG_MAX;

    // 使用sizeof运算符产生类型或变量的大小
    cout << \"int is \" << sizeof (int) << \" bytes.\" << endl;
    cout << \"short is \" << sizeof n_short << \" bytes.\" << endl;
    cout << \"long is \" << sizeof n_long << \" bytes.\" << endl;
    cout << \"long long is \" << sizeof n_llong << \" bytes.\" << endl;
    cout << endl;

    cout << \"Maximum values:\" << endl;
    cout << \"int: \" << n_int << endl;
    cout << \"short: \" << n_short << endl;
    cout << \"long: \" << n_long << endl;
    cout << \"long long: \" << n_llong << endl << endl;

    cout << \"Minimum int value = \" << INT_MIN << endl;
    cout << \"Bits per byte = \" << CHAR_BIT << endl;
    // cin.get();
    return 0;
}
//运行后,输出如下(运行自64位win10系统)
int is 4 bytes.
short is 2 bytes.
long is 4 bytes.
long long is 8 bytes.

Maximum values:
int: 2147483647
short: 32767
long: 2147483647
long long: 9223372036854775807

Minimum int value = -2147483648
Bits per byte = 8

     ·运算符sizeof:
          -以上输出表明,sizeof运算符指出在8位字节的系统中,int的长度为4个字节
          -可以对类型名和变量名使用sizeof运算符:
               对类型名(如int)使用sizeof运算符时,应将名称放在括号内
               变量名(如n_short)使用sizeof运算符时,括号时可选的
     ·头文件climits
          -头文件climits定义了符号常量来表示类型的限制
          -以上输出表明,INT_MAX(类型int能够存储的最大值)对于win10系统为2147483647
          -climits中的符号常量:
              char的位数CHAR_BIT;char的最大值CHAR_MAX;char的最小值CHAR_MIN;
              signed char的最大值SCHAR_MAX;signed char的最小值SCHAR_MIX
              unsigned char的最大值UCHAR_MAX;unsigned char的最小值UCHAR_MIN
              short的最大值SHRT_MAX;short的最小值SHRT_MIN
              unsigned的最大值USHRT_MAX;
              int的最大值INT_MAX;int的最小值INT_MIN;
              unsigned int的最大值UNIT_MAX;
              long的最大值LONG_MAX;long的最小值LONF_MIN;
              unsigned long的最大值ULONG_MAX;
              long long的最大值LLONG_MAX;long long的最小值LLONG_MAX;
              unsigned long long的最大值ULLong_MAX;
     ·初始化
          -将赋值与声明合在一起
          -可以使用字面值常量来初始化,如 int uncles = 5;
               可以将变量初始化为另一个已经定义过的变量,如 int aunts = uncles;
               可以使用表达式来初始化变量,如 int chairs = aunts + uncles +4
             C++特有的初始化语法,如: int wrens(432)
     ·C++11初始化方式(用于数组和结构,但在C++98中也可应于单值变量)
           如:int hamburgers = {24};          //将hamburgers设置为24
                 //可以省略等号,
                 int emus{7};                    //将emus设置为7
                 int rheas = {12};               //将rheas设置为12
                 //大括号内不包含任何东西,变量初始化为0
                 int rcs = {};                   //将rcs设置为0
                 int paychics{};                 //将paychics设置为03.1.4 无符号类型
(1)short、int、long和long long都有一种不能存储负值的无符号变体,
         其优点是可以增大变量能够存储的最大值
         例如:short表示的范围是-32768到32767,则无符号版本的表示范围为0到65535 
(2)创建无符号版本的基本整型,只需使用关键字unsigned来修改声明即可:
          unsigned short change;                //无符号short型
          unsigned int rovert;                  //无符号int型
          unsigned quarterback;                 //无符号int型,unsigned本身就是unsigned int
          unsigned long gone;                   //无符号long型
          unsigned long long lang_lang;         //无符号long long型

//以下程序演示了如何使用无符号类型,并说明了程序试图超越整型限制时将产生的后果
// exceed.cpp -- exceeding some integer limits
#include <iostream>
#define ZERO 0      // makes ZERO symbol for 0 value
#include <climits>  // defines INT_MAX as largest int value
int main()
{
    using namespace std;
    short sam = SHRT_MAX;     //将short型变量sam设置为最大的shot值
    unsigned short sue = sam; //将unsigned short型变量设置为sam的值(最大的shot值)

    cout << \"Sam has \" << sam << \" dollars and Sue has \" << sue;
    cout << \" dollars deposited.\" << endl
         << \"Add $1 to each account.\" << endl << \"Now \";
    sam = sam + 1;         //将sam+1,使其突破short的整型限制,输出变为-32768
    sue = sue + 1;         //将sue+1,并没有突破unsigned short的整型限制,输出为32768
    cout << \"Sam has \" << sam << \" dollars and Sue has \" << sue;
    cout << \" dollars deposited.\\nPoor Sam!\" << endl;
    sam = ZERO;            //将sam设置为0
    sue = ZERO;            //将sue设置为0
    cout << \"Sam has \" << sam << \" dollars and Sue has \" << sue;
    cout << \" dollars deposited.\" << endl;
    cout << \"Take $1 from each account.\" << endl << \"Now \";
    sam = sam - 1;         //将sam-1,并没有突破short的整型限制,输出为-1
    sue = sue - 1;         //将sue-1,突破了unsigned short的整型限制,输出为65535
    cout << \"Sam has \" << sam << \" dollars and Sue has \" << sue;
    cout << \" dollars deposited.\" << endl << \"Lucky Sue!\" << endl;
    // cin.get();
    return 0; 
}
//运行后,输出如下
Sam has 32767 dollars and Sue has 32767 dollars deposited.
Add $1 to each account.
Now Sam has -32768 dollars and Sue has 32768 dollars deposited.
Poor Sam!
Sam has 0 dollars and Sue has 0 dollars deposited.
Take $1 from each account.
Now Sam has -1 dollars and Sue has 65535 dollars deposited.
Lucky Sue!

    ·由以上程序及其输出结果可以看出,整型变量的行为如果超越了限制,其值将为另一端的取值(钟表式循环)

3.1.5 选择整型类型 
(1)int被设置为对目标计算机而言最为“自然”的长度
         自然长度指的是计算机处理起来效率最高的长度
         如果没有非常有说服力的理由选择其他类型,则应使用int
(2)如果变量表示的值不可能为负,可以使用无符号类型,可以表示更大的值
(3)若变量可能表示是整数值大于16位整数的最大可能值,则使用long,有助于提高可移植性
          如果要存储的值超过20亿,可使用long long
(4)short比int小,使用short可以节省内存。
(5)如果只需要一个字节,可以用char3.1.6 整型字面值
(1)整型字面值(常量)是显式地书写的常量
(2)C++能够以三种不同的计数方式来书写整数:
         C++使用前一(两)位来标识数字常量的基数
          如果第一位为1~9,则基数为10(十进制),如:93是以10为基数的
          如果第一位是0,第二位为1~7,则基数为8(八进制),如:042的基数为8,即十进制的34
          如果前两位为0x或0X,则基数为16(十六进制)如:0x42的基数为16,即十进制的66

//以下程序演示了这三种基数
// hexoct1.cpp -- shows hex and octal literals
#include <iostream>
int main()
{
    using namespace std;
    int chest = 42;     // decimal integer literal
    int waist = 0x42;   // hexadecimal integer literal
    int inseam = 042;   // octal integer literal

    cout << \"Monsieur cuts a striking figure!\\n\";
    cout << \"chest = \" << chest << \" (42 in decimal)\\n\";
    cout << \"waist = \" << waist << \" (0x42 in hex)\\n\";
    cout << \"inseam = \" << inseam << \" (042 in octal)\\n\";
    // cin.get();
    return 0; 
}
//运行后,输出如下
Monsieur cuts a striking figure!
chest = 42 (42 in decimal)
waist = 66 (0x42 in hex)
inseam = 34 (042 in octal)
    ·默认情况下,cout以十进制格式显示整数,而不管这些整数在程序中是如何书写的
     即,想要输出B000对应的十进制数,直接在cout中写为0xB000即可
    ·头文件iostream提供了控制符dec、hex和oct,分别用于指示以十/十六/八进制显示整数
//以下程序演示了使用hex和oct显示十进制值42
// hexoct2.cpp -- display values in hex and octal
#include <iostream>
using namespace std;
int main()
{
    using namespace std;
    int chest = 42;
    int waist = 42; 
    int inseam = 42;

    cout << \"Monsieur cuts a striking figure!\"  << endl;
    cout << \"chest = \" << chest << \" (decimal for 42)\" << endl;
    cout << hex;      // manipulator for changing number  
    cout << \"waist = \" << waist << \" (hexadecimal for 42)\" << endl;
    cout << oct;      // manipulator for changing number  
    cout << \"inseam = \" << inseam << \" (octal for 42)\" << endl;
    // cin.get();
    return 0; 
}
//运行后,输出如下
Monsieur cuts a striking figure!
chest = 42 (decimal for 42)
waist = 2a (hexadecimal for 42)
inseam = 52 (octal for 42)

      ·诸如cout << hex;等代码不会在屏幕上显示任何内容,而只是修改cout显示整数的消息
            控制符hex实际上是一条消息,告诉cout采取何种行动

3.1.7 C++如何确定常量的类型
(1)cout << \"Year = \" << 1492 << \"\\n\";
     如上述代码,在程序中使用常量表示一个数字,编译器将整型变量存储为int型
     除非有理由存储为其他类型
     (如使用了特殊的后缀来表示特定的类型,或者值太大,不能存储为int)
(2)后缀
     后缀是放在数字常量后面的字母,用于表示类型
      整数后面的l和L后缀表示该整数为long常量
      u或U后缀表示unsigned int常量
      ul、lu、UL、LU表示unsigned long常量(采用任何一种顺序,大小写均可)
      ll和LL表示long long常量
      ull、Ull、uLL和ULL表示unsigned long long常量
(3)长度
     ·对于不带后缀的十进制整数,
      将使用下面几种类型中能够存储该数的最小类型来表示:int、long、long long
     ·对于不带后缀的十六进制或八进制整数,
      将使用下面几种类型中能够存储该数的最小类型来表示:
       int、unsigned int、long、unsigned long、long long或unsigned long long3.1.8 char整型:字符和小整数
(1)char整型是专为存储字符(如字母和数字)而设计的
     char足够长,能够表示目标计算机系统中所有基本符号:字母、数字、标点符号等
(2)char最常用来处理字符,但也可以用做比short更小的整型

//以下程序使用了char类型
// chartype.cpp -- the char type
#include <iostream>
int main( )
{
    using namespace std;
    char ch;        // declare a char variable

    cout << \"Enter a character: \" << endl;
    cin >> ch;
    cout << \"Hola! \";
    cout << \"Thank you for the \" << ch << \" character.\" << endl;
    // cin.get();
    // cin.get();
    return 0;
}
//运行后,输出如下
Enter a character:
M                  //手动输入M
Hola! Thank you for the M character.

(3)程序中输入的是M,而不是M对应的字符编码(ASCII)77;程序将打印M,而不是77
         即,cin将键盘输入的M转换为77;cout将值77转换为所显示的字符M
         cin和cout的行为是由变量类型引导的
         如果将77存储在int变量中,则cout将它显示为77(显示两个字符7)

//以下程序对(3)进行了说明,还演示了如何在C++中书写字符字面值:将字符用单引号括起
// morechar.cpp -- the char type and int type contrasted
#include <iostream>
int main()
{
    using namespace std;
    char ch = \'M\';       // 将M的ASCII码赋值给char型变量ch
    int i = ch;          // 将相同的ASCII码赋值给int型变量i
    cout << \"The ASCII code for \" << ch << \" is \" << i << endl;

    cout << \"Add one to the character code:\" << endl;
    ch = ch + 1;          // 将ch+1赋值给ch
    i = ch;               // 将ch的新值赋值给i
    cout << \"The ASCII code for \" << ch << \" is \" << i << endl;

    // 使用cout.put()函数显示char类型的变量ch
    cout << \"Displaying char ch using cout.put(ch): \";
    cout.put(ch);

    // 使用cout.put()显示char常量
    cout.put(\'!\');

    cout << endl << \"Done\" << endl;
    // cin.get();
    return 0;
}
//运行后,输出如下
The ASCII code for M is 77
Add one to the character code:
The ASCII code for N is 78
Displaying char ch using cout.put(ch): N!
Done

         ·cin和cout的行为是由变量类型引导的
                 -\'M\'表示字符串M的数值编码,因此将char变量ch初始化为\'M\',将ch设置为77
                       然后将同样的值赋给int变量i,这样ch和i的值都是77
                       接下来,cout把ch显示为M,而把i显示为77,即值的类型将引导cout如何显示值
                 -ch实际上是一个整数,因此可以对它使用整数操作,如+1,这将ch的值变为78
                      然后程序将i的值重新设置为新的值。
                      cout再次将这个值的char版本显示为字符,将int版本显示为数字
         ·成员函数cout.put(),显示一个字符
                -cout.put(),即通过类ostream特定对象cout使用类ostream中的成员函数put()
                     成员函数是只能通过类的特定对象来使用的函数,必须使用句点将对象名和函数名称隔开
                -cout.put()提供了另一种显示字符的方法,可以替代<<运算符
(4)在C++中书写字符字面值:
         -对于常规字符(如字母、标点和数字),最简单的方法是将字符用单括号括起,
              表示字符的数值编码,例如:\'A\'为65,即字符A的ASCII码;\'a\'为97,即字符a的ASCII码
              这种表示方法优于数值编码,清晰且不需要知道编码方式(在不同的系统中\'A\'都对应A)
         -转义序列
              换行符\\n ; 水平制表符\\t ; 垂直制表符\\v ; 退格\\b ; 回车\\r ; 振铃\\a ;
              反斜杠\\\\ ; 问号\\? ; 单引号\\\' ; 双引号\\\"

//以下程序演示了一些转义序列
// bondini.cpp -- using escape sequences
#include <iostream>
int main()
{
    using namespace std;
    cout << \"\\aOperation \\\"HyperHype\\\" is now activated!\\n\";
    cout << \"Enter your agent code:________\\b\\b\\b\\b\\b\\b\\b\\b\";
       //8个下划线后面的8个\\b表示8次退格,即打印完下划线字符后将光标退到第一个下划线处
    long code;
    cin >> code;
    cout << \"\\aYou entered \" << code << \"...\\n\";
    cout << \"\\aCode verified! Proceed with Plan Z3!\\n\";
    // cin.get();
    // cin.get();
    return 0; 
}
//运行后,输出如下
Operation \"HyperHype\" is now activated!
Enter your agent code:42007007                //手动输入
You entered 42007007...
Code verified! Proceed with Plan Z3!

(5)通用字符名
         C++实现支持一个基本的源字符集和一个基本的执行字符集。
         C++标准还允许实现提供扩展源字符集和扩展执行字符集。
(6)signed char和unsigned char
         char在默认情况下既不是没有符号,也不是有符号,是否有符号由C++实现决定
         如果char有某种特定的行为,则可以显式地将类型设置为signed char或unsigned char
            例: char fodo;                             //可能是signed也可能是unsigned
                    unsigned char bar;                     //定义为unsigned
                    signed char snark;                     //定义为signed
         如果将char用作数值类型,unsigned char类型的表示范围通常是0~255,signed char的表示范围为-128到127
(7)wchar_t
          wchar_t类型可以表示扩展字符集,wcha_t类型是一种整数类型
          wchar_t有足够的空间,可以表示系统使用的最大扩展字符集
          wchar_t与另一种整型的长度和符号属性相同,对底层类型的选择取决于实现
          iostream头文件提供了与cin和cout作用相似的工具wcin和wcout处理wchar_t流
          可以通过加上前缀L来指示宽字符常量和宽字符串
              例: wchar_t bob = L\'P\';              //初始化一个宽字符串常量
                      wcout << L\"tall\" << endl;        //打印一个宽字符串
(8)C++11新增的类型char16_t和char32_t
         char16_t和char32_t,其中前者是无符号的,长16位,后者也是无符号的,长32位
         使用前缀u表示char16_t字符常量和字符串常量,如u\'C\'和u\'be good\'
         使用前缀U表示char31_t常量,如U\'R\'和U\"dirty rat\"
              例: char16_t ch1 = u\'q\';
                     char32_t ch2 = U\'\\U0000222B\';
           
3.1.9 bool类型
(1)布尔变量的值可以是true或false
(2)字面值true和false都可以通过提升转换为int类型,true转换为1,false转换为0
          例: int ans = true;                      //将1赋值给ans
                  int promise = false;                 //将0赋值给promise
(3)任何数字值或指针值都可以被隐式转换为bool值,非零值转换为true,零被转换为false
          例: bool start= -100;                    //将true赋值给start
                  bool stop = 0;                       //将false赋值给stop
     

3.2 const限定符
(1)C++处理符号常量的方法:使用const关键字来修改变量声明和初始化
         例: const int Months = 12;         //Month是一个值为12的符号常量
(2)常量被初始化后,其值就被固定了,编译器将不允许再修改该常量的值
         如果尝试修改常量的值,C++将指出不能给一个常量赋值
         关键字const叫做限定符,因为它限定了声明的含义
(3)对常量名称格式的几种约定:
         -名称首字母大写,如:Mouths
         -整个名称大写,如:MOUTHS
         -以字母k打头,如kmonths
         在阅读时有助于区分常量和变量
(4)创建常量的通用格式: const type name = value;
         应在声明中对const进行初始化,不要先声明再赋值
(5)如果在声明时没有提供值,则该常量的值将是不确定的,且无法修改

3.3 浮点数
(1)浮点数能够表示带小数部分的数字,且提供的值范围也更大。
     如果数字很大无法标识为long类型,则可以用浮点类型表示
(2)计算机将浮点数分成两部分存储:
         一部分(基准值)表示值,
         另一部分(基准因子)用于对值进行放大或缩小,即挪动小数点位置
     例: 34.1245可以表示为0.341245(基准值)和100(缩放因子)
          34124.5可以表示为0.341245(基准值)和10000(缩放因子)
     C++内部表示浮点数的方法与此相同,但是是基于二进制数,缩放因子是2的幂,不是10的幂3.3.1 书写浮点数
(1)标准小数点表示法
         例: 12.34
                 939001.32
(2)E表示法
     例: 3.45E6指的是3.45与10的6次方相乘的结果,即3450000;其中6被称为指数,3.45被称为尾数
     E表示法非常适合非常大和非常小的数
     E表示法确保数字以浮点格式存储,即使没有小数点。
     既可以使用E又可以使用e,指数可以是正数也可以是负数,但数字中不能有空格。
     d.dddE+n指将小数点向右移n位,d.dddE-n指将小数点向左移n位
     
3.3.2 浮点类型
(1)C++有3种浮点类型:float、double和long double
     这些类型是按他们可以表示的有效位数和允许的指数最小范围来描述的。
     有效位:数字中有意义的位
       float至少32位有效位,
       double至少48位有效位,且不少于float
       long double至少和double一样多
       三种类型的有效位数可以一样多

//以下程序演示了float和double类型及他们表示数字时在精度方面的差异(即有效位数)
// floatnum.cpp -- floating-point types
#include <iostream>
int main()
{
    using namespace std; 
    cout.setf(ios_ ::fixed, ios_ ::floatfield); // fixed-point
    float tub = 10.0 / 3.0;     // good to about 6 places
    double mint = 10.0 / 3.0;   // good to about 15 places
    const float million = 1.0e6;

    cout << \"tub = \" << tub;
    cout << \", a million tubs = \" << million * tub;
    cout << \",\\nand ten million tubs = \";
    cout << 10 * million * tub << endl;

    cout << \"mint = \" << mint << \" and a million mints = \";
    cout << million * mint << endl;
    // cin.get();
    return 0;
}
//运行后,输出如下
tub = 3.333333, a million tubs = 3333333.250000,
and ten million tubs = 33333332.000000
mint = 3.333333 and a million mints = 3333333.333333

       ·cout.setf()
           通常cout会删除结尾的0,例如将3333333.2500000显示为3333333.25
           调用cout.setf()将覆盖这种行为
      ·tub和mint都被初始化为10.0/3.0————3.3333333333……
           由于cout打印6位小数,因此tub和mint都是精确的
           但当程序将每个数乘以一百万后,
           float型的tub在第7个3之后就有了误差,
           double型的mint在第13个3时仍然是精确的
           说明float的精度比double的精度低

3.3.3 浮点常量
(1)在程序中书写浮点常量时,程序默认把它存储为double类型
     如果希望常量为float类型,则使用f或F后缀
     如果希望常量是long double类型,则使用l或L后缀
      例: 1.234f         // float型常量
              2.45E20F       // float型常量
              2.345324E28    //double型常量
              2.2L           //long double型常量3.3.4 浮点数的优缺点
(1)可以表示整数之间的值
(2)由于有缩放因子,因此可以表示的范围大得多
(3)浮点运算的速度比整数运算慢,且精度降低

//以下程序说明了浮点运算精度降低
// fltadd.cpp -- precision problems with float
#include <iostream>
int main()
{
    using namespace std;
    float a = 2.34E+22f;
    float b = a + 1.0f;

    cout << \"a = \" << a << endl;
    cout << \"b - a = \" << b - a << endl;
    // cin.get();
    return 0; 
}
//运行后,输出如下
a = 2.34e+22
b - a = 0

 ·2.34E+22是一个小数点左边有23位的数字;2.34E+22+1,即在第23位上+1
  但float类型只能表示数字中的前6位或前7位,因此修改第23位对这个值没有影响
3.4 C++算术运算符
(1)C++使用算数运算符来运算,它提供了5种基本的算术运算:加法、减法、乘法、除法、求模
         每种运算符都使用两个值(操作数)来计算结果,运算符及其操作数构成了表达式。
           例: int wheels = 4 + 2;
                   4和2都是操作数,+是加法运算符,4+2是一个表达式,其值为6
(2)C++中5种基本的运算符:
    · + 运算符对操作数执行加法运算    例如:4+20等于24
    · - 运算符从第一个数中减去第二个数    例如:12-3等于9
    · * 运算符将操作数相乘    例如:28*4等于112
    · / 运算符用第一个数除以第二个数    例如: 1000/5等于200;
           特别的,如果两个操作数都是整数,则结果为商的整数部分    例如:17/3等于5
    · % 运算符求模,即:生成第一个数除以第二个数后的余数    例如:19 % 6 等于1
           注意,两个操作数必须都是整型,将%用于浮点数将导致编译错误
           如果其中一个是负数,则结果的符号满足如下规则:(a/b)*b + a%b = a

//以下程序说明了变量和常量都可以用作操作数
// arith.cpp -- some C++ arithmetic
#include <iostream>
int main()
{
    using namespace std;
    float hats, heads;

    cout.setf(ios_ ::fixed, ios_ ::floatfield); // fixed-point
    cout << \"Enter a number: \";
    cin >> hats;
    cout << \"Enter another number: \";
    cin >> heads;

    cout << \"hats = \" << hats << \"; heads = \" << heads << endl;
    cout << \"hats + heads = \" << hats + heads << endl;
    cout << \"hats - heads = \" << hats - heads << endl;
    cout << \"hats * heads = \" << hats * heads << endl;
    cout << \"hats / heads = \" << hats / heads << endl;
    // cin.get();
    // cin.get();
    return 0;
}
//运行后,输出如下
Enter a number: 50.25                         //手动输入
Enter another number: 11.17                   //手动输入
hats = 50.250000; heads = 11.170000
hats + heads = 61.419998
hats - heads = 39.080002
hats * heads = 561.292480
hats / heads = 4.498657

3.4.1 运算符优先级和结合性
(1)算术运算符遵循通常的代数优先级:先乘除,后加减
     也可以使用括号来执行自己定义的优先级
(2)当两个运算符的优先级相同时,C++将看操作数的结合性是从左到右还是从右到左
     (乘除都是从左到右结合的)
(3)只有当两个运算符被作用于同一个操作数时,优先级和结合性规则才有效。
     如果不是这种情况,C++把这个问题留给了实现,让实现决定在系统中的最佳顺序

3.4.2 除法分支
(1)除法运算符(/)的行为取决于操作符的类型
      如果两个数都是整数,则C++将执行整数除法,即结果的小数点部分将被丢弃,结果为整数
      如果其中有一个(或两个)操作数是浮点值,则小数部分将被保留,结果为浮点数

//以下程序演示了C++除法如何处理不同类型的值
// divide.cpp -- integer and floating-point division
#include <iostream>
int main()
{
    using namespace std;
    cout.setf(ios_ ::fixed, ios_ ::floatfield);
    cout << \"Integer division: 9/5 = \" << 9 / 5  << endl;
    cout << \"Floating-point division: 9.0/5.0 = \";
    cout << 9.0 / 5.0 << endl;
    cout << \"Mixed division: 9.0/5 = \" << 9.0 / 5  << endl;
    cout << \"double constants: 1e7/9.0 = \";
    cout << 1.e7 / 9.0 <<  endl;
    cout << \"float constants: 1e7f/9.0f = \";
    cout << 1.e7f / 9.0f <<  endl;
    // cin.get();
    return 0;
}
//运行后,输出如下
Integer division: 9/5 = 1                         //整数9除以整数5结果为整数1
Floating-point division: 9.0/5.0 = 1.800000       //至少一个为浮点数
Mixed division: 9.0/5 = 1.800000                  //至少一个为浮点数
double constants: 1e7/9.0 = 1111111.111111        //两个操作数都是double,结果为double
float constants: 1e7f/9.0f = 1111111.125000       //两个操作数都是float,结果为float

3.4.3 求模运算符
(1)求模运算符返回整数除法的余数
     它与整数除法结合尤其适合解决要求将一个量分成不同的整数单元的问题
     (例如:单位转换问题,将X美元转换为A元B角C分D厘)

//以下程序使用整数除法来计算181磅里有多少英石,再用求模运算计算余下多少磅
// modulus.cpp -- uses % operator to convert lbs to stone
#include <iostream>
int main()
{
    using namespace std;
    const int Lbs_per_stn = 14;
    int lbs;

    cout << \"Enter your weight in pounds: \";
    cin >> lbs;
    int stone = lbs / Lbs_per_stn;      // whole stone
    int pounds = lbs % Lbs_per_stn;     // remainder in pounds
    cout << lbs << \" pounds are \" << stone
         << \" stone, \" << pounds << \" pound(s).\\n\";

    return 0; 
}
//运行后,输出如下
Enter your weight in pounds: 181
181 pounds are 12 stone, 13 pound(s).

3.4.4 类型转换
(1)对不同的类型进行计算时,C++自动执行很多多类型转换
     ·将一种算数类型的值赋给另一种算数类型的变量时,C++ 将对值进行转换
     ·表达式中包含不同的类型时,C++将对值进行转换
     ·将参数传递给函数时,C++将对值进行转换
(2)自动转换的详细规则
     ·初始化和赋值进行的转换
        -将一种类型的值赋给另一种类型的变量时,值将被转化为接受变量的类型
          例: long so_lang;
               short thirty = 1;
               so_long = thirty;    
               //将thirty的值赋给so_long时,将thirty的值先扩展为long,
               //再将扩展后得到的新值存储在so_long中
        -类似的转换有些是安全的,有些则会带来一些问题
              将较大的浮点类型转换为较小的浮点类型,如将double转换为float
                   精度(有效位数)降低;值可能会超出目标类型范围,此时结果将是不确定的
              将浮点型转化为整型,小数部分丢失;原来的值可能会超出目标类型范围,此时结果将是不确定的
             将较大的整型转换为较小的整型时,如将long转换为short,原来的值可能超出目标类型范围,通常只复制右边的字节

//以下程序演示了一些初始化进行的变换
// assign.cpp -- type changes on assignment
#include <iostream>
int main()
{
    using namespace std;
    cout.setf(ios_ ::fixed, ios_ ::floatfield);
    float tree = 3;     // int converted to float
    int guess = 3.9832; // float converted to int
    int debt = 7.2E12;  // result not defined in C++
    cout << \"tree = \" << tree << endl;
    cout << \"guess = \" << guess << endl;
    cout << \"debt = \" << debt << endl;
    // cin.get();
    return 0;
}
//运行后,输出如下
tree = 3.000000      //将整数3赋值给了浮点型tree,转换为3.000000
guess = 3            //将3.9832赋值给int变量guess。导致这个值被截取为3
                     //截取是直接丢弃小数部分,而不是四舍五入
debt = 1634811904    //将7E12赋值给int变量debt,无法进行存储,
                     //导致C++没有对结果进行定义的情况发生

       ·以{}方式初始化时进行的转换
           -C++11 将使用大括号的初始化称为列表初始化
                 因为这种初始化常用于给复杂的数据类型提供值列表
           -它对类型转换的要求更为严格,
                具体的说,列表初始化不允许缩窄,即变量的类型可能无法表示赋给它的值
                例如:不允许将浮点数转换为整型
       ·表达式中的转换
        当同一个表达式中包含两种不同的算数类型时,C++将执行两种自动转换:
           -一些类型在出现时便会自动转换
                在计算表达式时,C++将bool、char、unsigned char、signed char和short值转换为int
                               例如:true转换为1,false被转换为0,
                这些转换被称为整型提升,例如:
                               short chickens = 20;
                               short ducks = 35;
                               short fowl = chickens + ducks;
                               C++取得chicken和ducks的值并将它们转换为int,计算出结果后将结果转换为short
                其他的一些整型提升:
                               若short比int短,则unsigned short将被转换为int
                               若short和int长度相同,则unsigned short将被转换为unsigned int
                               确保了在对unsigned short进行提升时不会损失数据
          -不同类型进行算术运算时,也会进行一些转换
                当运算涉及两种类型时,较小的类型会被转换为较大的类型
                              例如: double 9.0 除以 int 5 ,先将int5转换为double5
                总之,编译器通过校验表来确定在算数表达式中执行的转换:
                              如果有一个为long double,则将另一个转换为long double
                              否则,有一个为double,则将另一个转换为double
                              否则,有一个为float,则将另一个转换为float
                              否则,说明操作数都是整型,因此执行整型提升
                              在这种情况下,
                                 若两个都是有符号或无符号的,且其中一个级别比另一个低,转换为级别高的类型
                                 若一个有符号另一个无符号,且无符号的级别比有符号高,转换为无符号那个的类型      
                                 否则,若有符号类型可表示无符号类型的所有可能取值,转换为有符号那个的类型
                                 否则,两个操作数都转换为有符号类型的无符号版本
                             注:整型级别
                                 有符号整型(由高到低):long long、long、int、short和signed char
                                 无符号整型的排列顺序与有符号整型相同
                                 类型char、signed char和unsigned char级别相同,类型bool的级别最低
       ·传递参数时的转换
           传递参数时的类型转换通常由C++函数原型控制,也可以取消原型对参数的控制(并不明智)
     
     ·强制类型转换
       通过强制类型转换机制显示地进行类型转换,强制类型转换的格式有两种:
         (long) thorn        //将存储在变量thorn中的值转换为long类型
         long (thorn)        //将存储在变量thorn中的值转换为long类型
       强制类型转换不会修改thorn变量本身,而是创建一个新的、指定变量的值
       可以在表达式中使用这个值,例如
         cout << int(\'Q\');   //显示整数Q
       强制类型转换的通用格式:
         (typeName) value    //将存储在变量value中的值转换为typeName类型
         typeName (value)    //将存储在变量value中的值转换为typeName类型
         其他4个强制类型转换运算符见第15章
         static_cast<typeName> (value)    //将存储在变量value中的值转换为typeName类型

//以下程序演示了两种基本的强制类型转换和static_cast<>
// typecast.cpp -- forcing type changes
#include <iostream>
int main()
{
    using namespace std;
    int auks, bats, coots;

    // the following statement adds the values as double,
    // then converts the result to int
    auks = 19.99 + 11.99;    //浮点数相加赋值给int型变量auks,结果被截短为31

    // these statements add values as int
    bats = (int) 19.99 + (int) 11.99;   //强制类型转换将浮点值转换为int后再求和
                                    //19.99和11.99分别被截短为19和11,所以求和结果为30
    coots = int (19.99) + int (11.99);  //强制类型转换将浮点值转换为int后再求和
                                    //19.99和11.99分别被截短为19和11,所以求和结果为30
    cout << \"auks = \" << auks << \", bats = \" << bats;
    cout << \", coots = \" << coots << endl;

    //通过强制类型转换显示char值的ASCII码
    char ch = \'Z\';
    cout << \"The code for \" << ch << \" is \";    // print as char
    cout << int(ch) << endl;    // print as int,将char类型强制转换为int
    cout << \"Yes, the code is \";
    cout << static_cast<int>(ch) << endl;// using static_cast,将char类型强制转换为int
    return 0; 
}
//运行后,输出如下
auks = 31, bats = 30, coots = 30
The code for Z is 90
Yes, the code is 90

    ·使用强制类型转换的两个原因:
          -可能有些值被存储为double类型,但希望计算时将其视为int
          -使用一种格式的数据能够满足不同的期望

3.4.5 C++11中的auto声明
(1)C++重新定义了auto的含义,让编译器能够根据初始值的类型推断变量的类型
(2)在初始化声明中,如果使用关键字auto,而不指定变量的类型,
     编译器将把变量的类型设置成与初始值相同
      例如: auto n = 100;           //n为int型
             auto x = 1.5;           //x为double型
             auto y = 1.3e12L;       //y为long double型
     如果将其用这种简单情形,会出现问题
      例如: auto x = 0.0            //x为double型
             double y = 0;           //y为double型0.0
             auto z = 0;            //z为int型,因为0为int型
     处理复杂问题,如标准模块库(STL)中的类型时,自动类型推断的优势才能显现

收藏 打印