Posts C++primer-p2 变量与基本类型
Post
Cancel

C++primer-p2 变量与基本类型

第二章 变量和基本类型

基本内置类型

基本算数类型

类型含义最小尺寸
bool布尔类型8bits
char字符8bits
wchar_t宽字符16bits
char16_tUnicode字符16bits
char32_tUnicode字符32bits
short短整型16bits
int整型16bits (在32位机器中是32bits)
long长整型32bits
long long长整型64bits (是在C++11中新定义的)
float单精度浮点数6位有效数字
double双精度浮点数10位有效数字
long double扩展精度浮点数10位有效数字

如何选择类型

  • 1.当明确知晓数值不可能是负数时,选用无符号类型;
  • 2.使用int执行整数运算。一般long的大小和int一样,而short常常显得太小。除非超过了int的范围,选择long long
  • 3.算术表达式中不要使用charbool
  • 4.浮点运算选用double

类型转换

  • 非布尔型赋给布尔型,初始值为0则结果为false,否则为true。
  • 布尔型赋给非布尔型,初始值为false结果为0,初始值为true结果为1。
  • 有符号数 超出定义时 结果显示为为定义的

不要混用有符号数与无符号数

1
2
3
4
5
6
// 无符号数与有符号数参与运算,有符号数会转换为无符号数
void test23(){
    unsigned i = 12;
    int j = -1;
    cout << i*j <<endl;  // 4294967284
}

字面值常量

一看便知,每个字面值常量对应一种数据类型

  • 一个形如42的值被称作字面值常量(literal)。
    • 整型和浮点型字面值。
    • 字符和字符串字面值。
      • 使用空格连接,继承自C。
      • 字符字面值:单引号, 'a'
      • 字符串字面值:双引号, "Hello World""
      • 分多行书写字符串。

        1
        2
        
        std:cout<<"wow, a really, really long string"
                  "literal that spans two lines" <<std::endl;
        
    • 转义序列。\n\t等。
    • 布尔字面值。truefalse
    • 指针字面值。nullptr

字符串型实际上时常量字符构成的数组,结尾处以'\0'结束,所以字符串类型实际上长度比内容多1。

变量

变量提供一个具名的、可供程序操作的存储空间。 C++变量对象一般可以互换使用。

变量定义(define)

  • 定义形式:类型说明符(type specifier) + 一个或多个变量名组成的列表。如int sum = 0, value, units_sold = 0;
  • 初始化(initialize):对象在创建时获得了一个特定的值。
    • 初始化不是赋值!
    • 初始化 = 创建变量 + 赋予初始值
    • 赋值 = 擦除对象的当前值 + 用新值代替
    • 列表初始化:使用花括号{},如int units_sold{0};
    • 默认初始化:定义时没有指定初始值会被默认初始化;在函数体内部的内置类型变量将不会被初始化
    • 类的初始化,其变量值由类的设计者自己决定。在后续的章节中说出。
    • 建议初始化每一个内置类型的变量。

变量的声明(declaration) vs 定义(define)

  • 为了支持分离式编译,C++将声明和定义区分开。声明使得名字为程序所知。定义负责创建与名字关联的实体。
  • extern:只是说明变量定义在其他地方。
    • 只声明而不定义: 在变量名前添加关键字 extern,如extern int i;。但如果包含了初始值,就变成了定义:extern double pi = 3.14;
    • 变量只能被定义一次,但是可以多次声明。定义只出现在一个文件中,其他文件使用该变量时需要对其声明。
  • 名字的作用域(namescope){}
    • 第一次使用变量时再定义它
    • 嵌套的作用域
      • 同时存在全局和局部变量时,已定义局部变量的作用域中可用::reused显式访问全局变量reused。
      • 但是用到全局变量时,尽量不适用重名的局部变量。

变量命名规范

  1. 需体现实际意义
  2. 变量名用小写字母
  3. 自定义类名用大写字母开头:Sales_item
  4. 标识符由多个单词组成,中间须有明确区分:student_loan或studentLoan,不要用studentloan。

左值和右值

  • 左值(l-value)可以出现在赋值语句的左边或者右边,比如变量;
  • 右值(r-value)只能出现在赋值语句的右边,比如常量。

复合类型

引用

引用不是对象

一般说的引用是指的左值引用

  • 引用:引用是一个对象的别名,引用类型引用(refer to)另外一种类型。如int &refVal = val;
  • 引用必须初始化。
  • 引用和它的初始值是绑定bind在一起的,而不是拷贝。一旦定义就不能更改绑定为其他的对象

指针

指针本来就是一个对象

1
2
3
4
5
    // 空指针问题 下面三者等价 
    // 建议初始化所有指针
    int *p = nullptr;
    int *p1 = 0;
    int *p2 = NULL;

int *p; //指向int型对象的指针

  • 是一种 "指向(point to)"另外一种类型的复合类型。

  • 定义指针类型: int *ip1;从右向左读有助于阅读ip1是指向int类型的指针。

  • 指针存放某个对象的地址

  • 获取对象的地址: int i=42; int *p = &i;&取地址符

  • 指针的类型与所指向的对象类型必须一致(均为同一类型int、double等)

  • 指针的值的四种状态:
    • 1.指向一个对象;
    • 2.指向紧邻对象的下一个位置;
    • 3.空指针;
    • 4.无效指针。
    • 对无效指针的操作均会引发错误,第二种和第三种虽为有效的,但理论上是不被允许的

  • 指针访问对象: cout << *p;输出p指针所指对象的数据, *解引用符

  • 空指针不指向任何对象。使用int *p=nullptr;来使用空指针。

  • 指针和引用的区别:引用本身并非一个对象,引用定义后就不能绑定到其他的对象了;指针并没有此限制,相当于变量一样使用。

  • 赋值语句永远改变的是左侧的对象。

  • void*指针可以存放任意对象的地址。因无类型,仅操作内存空间,对所存对象无法访问。

  • 其他指针类型必须要与所指对象严格匹配

  • 两个指针相减的类型是ptrdiff_t

  • 建议:初始化所有指针。

  • int* p1, p2;//*是对p1的修饰,所以p2还是int型

    指针是对象,所以存在对指针的引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
        //1. 指向指针的指针
        int i = 12;
        int *p = &i;
        int **r = &p;
        cout << p << endl;
        cout << r << endl;
    
        //2. 指向指针的引用
        int i = 12;
        int *p = &i;
        int *&r = p;
        *r = 1;
        cout << i << endl;
    
  • void * 指针 可以存放任意对象的地址。单是能做的事情比较有限:和别指针比较,作为函数的输入输出,或者给另外一个void*赋值

const限定符

  • 动机:希望定义一些不能被改变值的变量。

初始化和const

  • const对象必须初始化,且不能被改变
  • const变量默认不能被其他文件访问,非要访问,必须在指定const定义之前加extern。要想在多个文件中使用const变量共享,定义和声明都加const关键字即可。
1
2
extern const int con = 25;  // 定义
extern const int con;   // 在头文件中声明

const的引用

对常量的引用就是对const的引用

  • reference to const(对常量的引用):指向const对象的引用,如 const int ival=1; const int &refVal = ival;,可以读取但不能修改refVal
  • 临时量(temporary)对象:当编译器需要一个空间来暂存表达式的求值结果时,临时创建的一个未命名的对象。
  • 对临时量的引用是非法行为。
  • const引用可指向一个非const对象

    1
    2
    3
    4
    5
    6
    7
    
      void test01(){
          int i  = 3;
          const int &s = i; // 3
          cout << s << endl;
          i = 12;
          cout << s << endl; // 12
      }
    
  • 但是:

    1
    2
    3
    4
    5
    
      void test02(){
          int i = 12;
          const int j = i;
          int *p = &j;  // 错误
      }
    

指针和const

  • pointer to const(指向常量的指针):不能用于改变其所指对象的值, 如 const double pi = 3.14; const double *cptr = &pi;
  • const pointer:指针本身是常量,也就是说指针固定指向该对象,(存放在指针中的地址不变,地址所对应的那个对象值可以修改)如 int i = 0; int *const ptr = &i;

*指向常量的指针常量的引用 一样,并没有规定其指向必须是一个常量。 都不过是自以为是,自己不能更改,可通过其他方式更改。

1
2
3
4
5
6
7
8
  // 指向常量的指针
  void test227(){
      int i = 12;
      const int *p  = &i;   
      int j  = 11;
  //    *p = &j; // 错误
      i = 11;  // 正确
  }

顶层const

  • 顶层const:指针本身是个常量。 (常量指针)
  • 底层const:指针指向的对象是个常量。拷贝时严格要求相同的底层const资格。(指向常量的指针)

constexpr和常量表达式(▲可选)

  • 常量表达式:指值不会改变,且在编译过程中就能得到计算结果的表达式。

    1
    2
    3
    4
    
    const int i = 12;  // 是
    const int s = i + 1;  // 是
    int ss = 123;  // 不是
    const int sz = get_size();  // 不是
    
  • C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量的表达式。

    1
    
    constexpr int pf = size();
    

处理类型

类型别名

  • 传统别名:使用typedef来定义类型的同义词。 typedef double wages;
  • 新标准别名:别名声明(alias declaration): using SI = Sales_item;(C++11)
1
2
3
4
5
6
7
8
// 对于复合类型(指针等)不能代回原式来进行理解
typedef char *pstring;  // pstring是char*的别名
const pstring cstr = 0; // 指向char的常量指针
// 如改写为const char *cstr = 0;不正确,为指向const char的指针

// 辅助理解(可代回后加括号)
// const pstring cstr = 0;代回后const (char *) cstr = 0;
// const char *cstr = 0;即为(const char *) cstr = 0;

auto类型说明符 c++11

  • auto类型说明符:让编译器自动推断类型
  • 一条声明语句只能有一个数据类型,所以一个auto声明多个变量时只能相同的变量类型(包括复杂类型&和*)。auto sz = 0, pi =3.14//错误
  • int i = 0, &r = i; auto a = r; 推断a的类型是int
  • 会忽略顶层const
  • const int ci = 1; const auto f = ci;推断类型是int,如果希望是顶层const需要自己加const
1
2
3
4
5
6
7
// auto 的顶层指针会被忽略掉
void test230(){
    const int i = 12;
    auto j = i;
    j = 15;
    cout << j << endl;   // auto 自动忽略掉了顶层const j=15
}

decltype类型指示符

  • 从表达式的类型推断出要定义的变量的类型。
  • decltype:选择并返回操作数的数据类型
  • decltype(f()) sum = x; 推断sum的类型是函数f的返回类型。
  • 不会忽略顶层const
  • 如果对变量加括号,编译器会将其认为是一个表达式,如int i–>(i),则decltype((i))得到结果为int&引用。
  • 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型。也就是说,如果 i 是 int,则表达式 i=x 的类型是 int&。
  • C++11

自定义数据结构

struct

尽量不要吧类定义和对象定义放在一起。如struct Student{} xiaoming,xiaofang;

  • 类可以以关键字struct开始,紧跟类名和类体。
  • 类数据成员:类体定义类的成员。
  • C++11:可以为类数据成员提供一个类内初始值(in-class initializer)。

编写自己的头文件

  • 头文件通常包含哪些只能被定义一次的实体:类、constconstexpr变量。

预处理器概述:

  • 预处理器(preprocessor):确保头文件多次包含仍能安全工作。
  • 当预处理器看到#include标记时,会用指定的头文件内容代替#include
  • 头文件保护符(header guard):头文件保护符依赖于预处理变量的状态:已定义和未定义。
    • #indef已定义时为真
    • #inndef未定义时为真
    • 头文件保护符的名称需要唯一,且保持全部大写。养成良好习惯,不论是否该头文件被包含,要加保护符。
1
2
3
4
5
6
#ifndef SALES_DATA_H  //SALES_DATA_H未定义时为真
#define SALES_DATA_H
strct Sale_data{
    ...
}
#endif
This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags