C++ primer 第五版个人笔记 第二章 变量和基本类型

2.1 基本内置类型

  1. wchar_t, char16_t, char32_t 属于算术类型,最小尺寸见30页
  2. long long 在C++ 11中新定义
  3. 大多数机器的字节(byte)由8比特(bit)构成,字(word)则由32或64比特(bit)构成,也就是4或者8字节,31页
  4. 类型选择几个建议:1明确数值不为负时选无符号类型(unsigned ___);
  5. 2 整数运算一般不用short而是int, 当数值范围超过int, 用long long;
  6. 3 算术表达式中尽量不用 char 或者bool , 因为char类型在某些机器上是有符号的,另一些机器是无符号的,容易出现差错;
  7. 4 浮点运算用double 少用float ,两者计算代价相差无几甚至有些机器double比float还快, long double一般不用因为精度需求没那么高
  8. unsigned char c = -1, 假设char 占 8 比特, c的值为255,  (-1的补码表示为11111111,如果是无符号表示就是255)
  9. signed char c2 = 256, 假设char 占8比特,c2的值是未定义的 (256无法用8位表示) 33页
  10. unsigned u =10;
    int i = -42;
    std::cout<<u+i<<std::endl;
  11. 上面代码输出的值,如果int占32位,结果为2^32-32;
  12. 如果两个字符串字面值位置紧邻且仅由空格、缩进和换行符分隔,则它们实际上是一个整体,因此可以分多行书写字符串字面值  36页
  13. 泛化的转义序列形式是\x后跟1个或多个十六进制数字,\后紧跟1个或2个或3个八进制数字(只会判断最多3位);
  14. 练习2.6 int month=09, 此处为无效的八进制数字

2.2 变量

  1. C++语言中用等号=来初始化和复制是两个完全不同的操作,尽管在很多编程语言里几乎可以忽略二者的区别;
  2. 列表初始化且初始值存在丢失信息的风险时,编译器将报错 40页:
    int main()
    {
        long double ld = 3.141592654;
        int a{ ld },        //报错C2397	从“long double”转换到“int”需要收缩转换	
            b = { ld };
        int c(ld),
            d = ld;
        cout << a << endl << b << endl << c << endl << d << endl;
        return 0;
    }

     

  3.  变量能且只能被定义一次,但是可以被多次声明,使用extern关键字声明一个变量而不显式去定义,在函数体内部,如果视图初始化一个有extern关键字标记的变量,将引发错误;

    extern int global_int;
    
    int main()
    {
        global_int = 2;    //LNK2001	无法解析的外部符号 "int global_int" 
        std::cout << global_int << std::endl;
        return 0;
    }

    如果要在多个文件中使用同一个变量,就必须将声明和定义分离,此时变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。 41页

  4.  C++用户自定义标识符中不能连续出现两个下划线、也不能以下划线紧连大写字母开头(这里我在VS2019试了一下貌似是可以的,不过还是尽量不要这么操作把,数字开头是会报错的) 47页

 

显式访问全局变量可以在前面加::作用域操作符,因为全局作用域本身没有名字,所以当作用域操作符的左侧为空时,向全局作用域发出请求获取作用域操作符右侧名字对应的变量;

2.3 复合类型

  1. 一条声明语句有一个基本数据类型(int, double, char等)和紧随其后的一个声明符列表(包括*,& 后跟变量名)组成,因此当声明引用或者声明指针的时候如 int *a 应该看做 int (*a) 而不是 (int*) a,这也能解释一些用逗号(,)连接的连续声明的语句,*和&可以看作声明符,a可以看作变量标识符,声明符+变量标识符构成了声明符列表,如下
    int i = 1024 , i2=2048;  //i 和 i2都是int
    int &r = i, r2=i2 // r是一个引用,与i绑定在一起, r2是int
    int i3 = 1024, &ri = i3 // i3是int, ri是一个引用,与i3绑定在一起

     

  2. 像* 和 &这样的符号,既能用作表达式里的运算符,也能作为声明的一部分出现,意义由符号的上下文决定;

  3.  nullptr 是C++11新标准引入的方法以前的空指针声明有两种方式,现在加入nullptr之后有三种:

     int *p1 = nullptr;
     int *p2 = 0; //此处的0是字面常量,不能用值为0的int类型代替
     int *p3 =NULL;

    在可能的情况下,尽量初始化所有的指针,如果不清楚指向何处,就初始化为nullptr或者0, 49页

  4. 任何非零指针对应的bool条件值都是true;

  5.  引用本身不是一个对象,因此不能定义指向引用的指针,但指针是对象,所以存在对指针的引用

  6.  

    面对一条比较复杂的指针或者引用的声明语句时,从右向左阅读有助于弄清楚它的真是含义,如下代码

     

     

    int i = 42;
    int *p;        //p是int型指针
    int *&r = p;    //r是引用,引用的是int型的指针
    
    r = &i;       //把i的地址赋给p
    *r =0 ;        //p指向的值改为0

    从右向左看,离r最近的是&,说明r是一个引用,其余部分用于确定引用的类型是什么

2.4 const限定符

  1. const声明的对象与非const限定符声明的对象的限制主要在于不能执行改变其内容的操作, const int 和普通的int一样都能参与算术运算、也都能转换成一个bool值;还有一种是初始化,可以用const int 初始化 int 也可以用int 初始化 const int;
  2. 引用的类型必须与其所引用对象的类型一致,有两个例外,第一个是初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可
    int i= 42;
    const int &r1 = i;  //允许将const int&绑定在一个普通int对象上
    const int &r2 = 42; //正确,r1是一个常量引用
    const int &r3 = r1 * 2 //正确, r3是一个常量引用
    int &r4 = r1 * 2 //错误,r4是一个普通的非常量引用,只能引用 int型变量
    
    double dval = 3.14
    const int &ri = dval; //正确, double型的dval 变量可以转换成int型
    //实际上 上面两行的过程是这样的
    const int temp = dval;
    const int &ri = temp;

    虽然const int &可以引用double型变量,但是还是会得到一个警告, 

    int main()
    {
        int i = 3;
        double dval = 3.14;
        const int& ri = i;
        int& ri2 = i;
        //ri = 4;
        std::cout << ri << std::endl; //输出4
    
        return 0;
    }

    此处可以用ri2来改变i的值,不能通过改ri来改i的值,因此const int&声明的变量不能作为=赋值运算符的左值;

  3.  指向常量的指针不能用于改变所指对象的值,要想存放常量对象的地址,只能使用指向常量的指针(即指针不能作为左值来更改其所指对象的值) 详见56页;

  4. 和常量引用一样,指向常数的指针也没有规定其所指的对象必须是一个常量,所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他途径改变

    double pi = 3.14159;
    double dval = 3.14;
    const double *cptr = &dval ;
    *cptr = 42;  //错误,不能通过*cptr改变dval的值
    cptr = pi    //正确, cptr可以指向别处,但是不能通过*cptr改变别处的值

    这里的理解是一个难点,详见56页 

  5.  

    顶层const与底层const的区别在于,顶层const表示该const修饰的对象是一个常量,不可更改,底层const表示指针所指的对象为常量,因此底层const只在指针*和引用&中存在,

     

     

    int i =0;
    int *const p1= &i; //不能改变p1的值,顶层const
    const int ci = 42; //不能改变ci的值,顶层const
    const int *p2 = &ci; //可以改变p2的值,不能改变*p2的值,底层const
    const int *const p3 = p2; //靠右的是顶层const, 靠左的是底层const
    const int &r = ci; //所有声明引用的const都是底层const,因为引用不是对象,不存在指针的多种情况

     

     

  6. 如果认定变量是一个常量表达式,就把它生命诚constexpr类型(C++11新特性)

  7. 尽管指针和引用都能定义成constexpr,但他们的初始值受到严格限制。一个constexpr指针的初始值必须是nullptr或者0,或者是储存于某个固定地址中的对象;

  8. constexpr把它所定义的对象置为了顶层const,如下代码可以用来区分

    const int *p = nullptr;  //p是一个指向整型常量的指针
    constexpr int *q = nullptr; //q是一个指向整数的常量指针

     

2.5 处理类型

  1. 类型别名两种定义方法,一种是使用关键字typedef,

    typedef double wages;  //wages是double的同义词
    typedef wages base, *p //base是double的同义词, p是double*的同义词
    
    //typedef语句和前面的声明语句一样,后面跟的声明符可以包含类型修饰(如*,&)

    另一种C++11新标准是使用using

    using SI = Sales_item; // SI是Sales_item的同义词,等价于 typedef Sales_item SI;

     

     

  2. 当用别名指代某个复合类型或常量时,容易产生意想不到的结果,理解上应该弄清楚,下例:

    typedef char *pstring;
    const pstring cstr = 0 ;  // cstr是指向char类型的常量指针,不能改cstr值,即cstr不能为左值,此处的const为顶层const
    const char *cstr = 0;  // cstr是指向const char类型的指针, 不能通过*cstr改值, 即*cstr不能为左值,此处的const为底层const

    详见61页

  3. auto 类型说明符(C++11新特性)让编译器通过初始值来推算变量的类型,因此auto定义的变量必须有初始值;

  4. auto可以在一条语句中声明多个变量,但是所有变量的初始数据类型必须一样,尽量不要在一条语句中声明;

  5. decltype 和auto的区别,详见63页,有一种情况需要注意就是decltype((variable))双层括号的结果永远是引用,而decltype(variable)的结果只有当variable本身是引用时才是引用;因为(variable)可以看作一个赋值表达式,而赋值表达式的类型就是引用;

  6. decltype()表达式内容是解引用操作,也将得到引用类型,如decltype(*p)得到的是p指针所指对象的引用;

  7. 练习2.38举例表示一种decltype和auto指定类型不一样

        int c = 1;
        const int ci = 0, & cj = ci;
        auto ck1 = cj;              // ck1为 int, 可以成为左值赋值
        decltype(cj) ck2 = c;       // ck2为 const int& ,不可以成为左值赋值
        ck1 = 2;                // 正确
        ck2 = 3;                // 错误,ck2无法赋值

     

2.6 自定义数据结构

  1. 类体右侧表示结束的花括号后必须写一个分号,这是因为类体后面可以紧跟变量名以示对该类型对象的定义,所以分号必不可少;分号表示声明符的结束。 

  2. 预处理器(preprocessor)是在编译之前执行的一段程序,可以部分地改变我们写的程序,#include就是意向预处理功能;

  3. 写头文件时要设置保护符,#ifdef, #define, #ifndef, #endif

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值