C++-《C++ Primer》总结01

第二章 变量和数据类型

1 基本内置类型/基本数据类型

1.1 基本数据类型
  • 基本数据类型 = 算数类型 + 空类型(void)
  • 算术类型= 整型 (广义,见下)+ 浮点型(float/double/long double)
  • 整型 = 整型(short/int/long/long long)+ 字符型(char/wchar_t/char16_t/char32_t)+ 布尔型(bool)
  • 字符型 = 基本字符型(char)+ 扩展字符型(wchar_t/char16_t/char32_t)
  • 整型(广义,除去布尔型和扩展字符型)= 带符号整型(广义,signed) + 无符号整型(广义,unsigned)
  • 字面值常量 = 整型和浮点型字面值 + 字符和字符串字面值(‘x’/“xxx”) + 转义序列(不可打印字符和特殊含义字符) + 布尔字面值(true/false)和指针字面值(nullptr)
  • 算术类型转换
    在这里插入图片描述
    在这里插入图片描述
1.2 算术类型
  • 声明、定义和初始化:声明规定了对象的类型和名字,定义在声明的基础上申请存储空间。初始化则在定义的基础上存储空间赋了一个初始值;
	externa int i; //声明i,而非定义i;特点是在变量类型前添加关键字extern,并且不显示地初始化对象;用于跨文件分离式编译;用在头文件当中;
	int j1; //声明并定义j1
	externa int j2 = 5; //声明并定义j2,用于跨文件分离式编译
	int k = 5; //定义并初始化变量k
  • 初始化和赋值:初始化是赋初值,赋值是赋新值;
  • 一般表达式:由算术类型对象和算术运算符构成的表达式,可以理解为临时量对象(等效理解为字面值常量);
  • 算术类型非常量(即普通非常量)初始值来源:字面值常量 / 算术类型对象(包括普通非常量和普通常量,算术类型广义一致) / 一般表达式 / 普通引用 / 常量引用,不能是指针对象(包括常量指针、指向常量的指针和普通非常量指针以及地址);
  • 算术类型常量(即普通常量,属于const对象,带有const限定词的普通变量,区别于字面值常量。由定义知,其值在初始化后便不能再被修改)初始值来源:字面值常量 / 算术类型对象(包括普通非常量和普通常量,算术类型广义一致) / 一般表达式 / 普通引用 / 常量引用,不能是指针对象(包括常量指针、指向常量的指针和普通非常量指针以及地址);
  • 算术类型转换运算是算术类型对象(包括普通常量和普通非常量)独有的,比如复合类型就没有类型转换运算。另外,可以用不同算术类型的普通常量给普通非常量初始化,也可以用不同算术类型的普通非常量给普通常量初始化。
	//普通非常量
    int r1 = 1; //true,利用字面值常量完成初始化
	float f1 = r1; //true,利用int型非常量(为普通非常量)对象完成初始化
	double d1 = 1.34; //true,利用字面值常量完成初始化
	double d2 = d1; //true,利用普通非常量完成初始化
	const double d3 = 2;
	double d4 = d3; //true,利用普通常量完成初始化
	double d5 = d1 * 2; //true,利用一般表达式完成初始化
  	double &d6 = d1;
  	double d7 = d6; //true,利用普通引用完成初始化
  	const double &d8 = 5;
  	double d9 = d8; //true,利用常量引用完成初始化
	int *p = &r1;
    int i1 = p; //false,不能用指针初始化普通非常量
    int i2 = &r1;  //false,不能用地址初始化普通非常量
	//普通常量
  	int i0 = 42;
	const int i1; //false,i1是一个未经初始化的int型常量
	const int i2 = 42; //true,利用字面值常量完成普通常量初始化
	const int i3 = i0; //true,利用普通非常量完成普通常量的初始化
	const int i4 = i2; //true,利用普通常量完成普通常量的初始化
	const int i5 = i2 * 2; //true,利用一般表达式完成普通常量的初始化
	extern const int i6; //true,用于头文件当中,注意到有限定符且未显示定义,因此是跨文件声明某个整型常量已经被定义
  	int &i7 =i0;
 	const int i8 = i7; //true,利用普通引用完成初始化
  	const int &i9 = 5;
  	const int i10 = i9; //true,利用常量引用完成初始化
	int *p = &i0;
	const int i11 = p; //false,不能用普通非常量指针初始化普通常量
    const int i12 = &r1;  //false,不能用地址初始化普通常量
  • 对于基本内置类型,在块作用域内定义的变量如果没有被初始化,将拥有一个不确定的值;
  • const 对象(或const 变量,带有const限定的变量,字面值常量不需要const限定)被定义后其值就不能再改变,因此必须在其被定义的同一条语句内完成初始化;

2 引用类型和指针类型

2.1 复合类型
  • 复合类型:包括引用类型和指针类型,指基于其他类型所定义的类型;
  • 复合类型的声明
	int i = 1024, &r = i, *p1 = &i, *const p2 = &i; 
- int、int & 、int *、int *const:类型;
- int:基本数据类型/算术类型;
- i、&r、*p1、*const p2:声明符;
- i、r、p1、p2:变量标识符;
- &、*、*const:类型修饰符;
- &:引用声明符;
- *:指针声明符;
2.2 引用类型
  • 引用类型:引用另外一种对象的类型;
  • 引用被定义后其值就不能再改变,因此必须在其被定义的同一条语句内完成初始化;
  • 引用是所引用对象的别名,因此引用本身不是一个对象。又因为引用不是一个对象,所以对引用的引用是错误的,而指针是一个对象,故对指针的引用是正确的;
  • 引用(准确说是普通引用)的初始值来源:算术类型非常量(即普通非常量,不能是普通常量,算术类型不一致)或 同算术类型普通引用(不能是常量引用,算数类型不一致),不能是字面值常量或一般表达式;
	int &r1; //false,r1没有初始化
	int i1 = 1024, &r2 = i1; //true,初始值为普通非常量
	int &r4 = r2; //true,本质是对r所绑定对象的引用,不要简单理解为对引用的引用
	int &r3 = 1024; //false,初始值不能为字面值常量
	int &r3 = r2 * 2; //false,初始值不能为一般表达式
	const int i2 = 1024;
	int &r4 = i2; //false,初始值不能为常量引用
  • 常量引用(引用本身不是一个对象,也就无所谓常量还是非常量,应该理解为对普通常量的引用。由定义知,对常量的引用不能用于修改其所绑定的对象,具体说是普通非常量对象,但其所绑定的对象数值是可以被其他途径修改的)的初始值来源:字面值常量 / 算术类型对象(包括普通非常量和普通常量,算术类型广义一致) / 一般表达式 / 广义同算术类型引用(包括普通引用和常量引用),不能是指针对象(包括常量指针、指向常量的指针和普通非常量指针以及地址);
	int i1 = 42;
	const int i2 = 55;
	int &r0 = i1; 
	const int &r1 = 42; //true,初始值为字面值常量
	const int &r2 = i1; //true,初始值为普通非常量
	const int &r3 = i2; //true,初始值为普通常量
	const int &r4 = i1 * 2; //true,初始值为一般表达式
	const int &r5 = r0; //true,初始值为广义同类型引用-普通引用,反之不行
	const int &r6 = r1; //true,初始值为同类型引用-常量引用
  • 常量引用仅对引用可参与的操作作出了限定, 对于引用的对象本身是不是一个常量未做限定。因此,对于所绑定的普通非常量对象而言,是可以通过其他途径改变变量的值,而非常量值修改后与引用的值是否一致,就看是否满足广义一致了。
	//算术类型广义一致,本例中是int-const int&
	int i = 42;
  	int &r1 = i;
  	const int &r2 = i;
  	std::cout << "r1 = " << r1 <<", r2 = " << r2 << std::endl; //r1 = 42, r2 = 42
  	r1 = 1;
  	std::cout << "r1 = " << r1 <<", r2 = " << r2 << std::endl; //r1 = 1, r2 = 1,可以通过改变i/r1的值改变r2的值,反之不行;

	int &r3 = i;
  	const int &r4 = i * 2; //这里是“i * 2”,不是“i”,“i * 2”是一个临时量(为普通非常量),此处认为是算术类型不一致
  	std::cout << "r3 = " << r3 <<", r4 = " << r4 << std::endl; //r1 = 42, r2 = 84
  	r3 = 1;
  	std::cout << "r3 = " << r3 <<", r4 = " << r4 << std::endl; //r1 = 1, r2 = 84,改变i/r3的值无法改变r4的值
	//算术类型不一致,例如double-const int&
	double dval = 3.14;
	const int &ri = dval; //true,初始值为普通非常量
	
	//内部编译器处理:引入了临时量(为普通常量)
	double dval = 3.14;
	const int temp = dval;
	const int &r = temp;
	
 	double dval = 3.14;
  	double &r5 = dval;
  	const int &r6 = dval;
  	std::cout << "r5 = " << r5 <<", r6 = " << r6 << std::endl; //r5 = 3.14, r6 = 3
  	r5 = 1.23;
  	std::cout << "r5 = " << r5 <<", r6 = " << r6 << std::endl; //r5 = 1.23, r6 = 3,改变dval/r5的值无法改变r6的值
  • 绑定到指针的引用的初始值来源:指针(准确说是普通非常量指针) / 指向指针的指针/ 指向指针的指针的指针等;
    int ival = 1024;
    int *pi = &ival;
    int **ppi = &pi;
    int *&a = pi; //true,绑定到指针(准确说是普通非常量指针)
    int **&b = ppi; //true,绑定到指向指针的指针
2.3 指针类型
  • 指针类型:指向另外一种对象的类型;
  • 对于指针类型,在块作用域内定义的指针如果没有被初始化,将拥有一个不确定的值;
  • 对于指针类型,没有 &字面值常量 或 &一般表达式 写法。除了0外,也不能用字面值常量或一般表达式直接初始化普通指针
  • 指针(准确说是普通非常量指针)的初始值来源:地址(&普通非常量,要求算术类型一致) 或 同算术类型指针(包括普通非常量指针和常量指针,不包括指向常量的指针,算术类型不一致);
	double dval = 3.14;
	double *const p0 = &dval;
	double *pd1 = &dval; //true,利用&普通非常量(为地址)完成初始化 
	double *pd2 = pd1; //true,利用普通指针完成初始化
	double *pd3 = pd1; //true,利用常量指针完成初始化
	double *pd4 = &pd1; //false,pd4是指向对象的指针,不能赋值指向指针的指针
  • 空指针的初始值来源:0 / NULL / nullptr;
	int *p1 = nullptr; //true,等价于int *p1 = 0 
	int *p2 = 0; //true,利用字面值常量0初始化p2
	int *p3 = NULL; //true,,等价于int *p3 = 0
  • void* 指针的初始值来源:可以存放任意对象(包括普通常量和普通非常量)的地址,但不能直接操作void*指针所指的对象;
	double  obj = 3.14, *pd = &obj;
	void *pv = &obj; //true,void*可以存放任意对象的地址
	pv = pd; //true,pv可以存放任意对象的地址
	*pv = 3.1415 //false,不能直接操作void*指针所指的对象
  • 指向指针的指针的初始值来源:地址(&普通非常量指针,不能是&常量指针 和 &指向常量的指针)或 指向指针的指针;
    double dval = 102.4; //普通非常量
	double *p1 = &dval; //普通指针
    double *const p2 = &dval; //常量指针
    const double *p3 = &dval; //指向常量的指针
	double **pp1 = &p1; //true,利用&普通非常量指针(为地址)完成初始化
	double **pp2 = &p2; //false,不能用&常量指针(为地址)完成初始化,类型不一致
	double **pp3 = &p3; //false,不能用&指向常量的指针(为地址)完成初始化,类型不一致
	double **pp4 = pp1; //true,利用指向指针的指针完成初始化
  • 指向常量的指针(具体说是普通常量,由定义知,指向常量的指针不能用于修改其所指向的对象,但其所指向的值是可以被其他途径修改的,且其所指向的对象也是可以修改的)的初始值来源:地址(包括&普通非常量 和 &普通常量,算术类型广义一致)或 广义同算术类型指针(包括普通非常量指针、指向常量的指针和常量指针);
	int i1 = 42; //普通非常量
	int *p00 = &i1; //普通非常量指针
	int *const p01 = &i1; //常量指针
    const int i2 = 55; //普通常量
    const int *const p02 = &i2; //常量指针
	
	const int *p1 = &i1; //true,初始值为&普通非常量(为地址)
	const int *p2 = &i2; //true,初始值为&普通常量(为地址)
	const int *p3 = p00; //true,初始值为广义同类型指针-普通非常量指针,反之不行
	const int *p4 = p2; //true,初始值为同类型指针-指向常量的指针
	const int *p5 = p01; //true,初始值为广义同类型指针-常量指针
	const int *p6 = p02; //true,初始值为同类型指针-常量指针
  • 指向常量的指针仅对指针可参与的操作作出了限定, 对于指向的对象本身是不是一个常量未做限定。因此,对于所指向的非常量对象而言,是可以通过其他途径改变非常量的值,而非常量值修改后指针指向的值是否一致,就看是否满足广义一致了。
	//算术类型广义一致,例如int-const int*
	int i = 42;
  	int *p1 = &i;
  	const int *p2 = &i;
  	std::cout << "*p1 = " << *p1 <<", *p2 = " << *p2 << std::endl; //*p1 = 42,*p2 = 42
  	*p1 = 1;
  	std::cout << "*p1 = " << *p1 <<", *p2 = " << *p2 << std::endl; //*p1 = 1,*p2 = 1,可以通过改变i/r1的值改变r2的值,反之不行;
	//算术类型不一致,例如double-const int*
	double dval = 3.14;
	const int *pi = &dval; //false,指向常量的指针在编译过程中,没有引入临时量这个过程,因此,类型不一致无法通过编译
	
	//修改:手动引入临时量对象(为普通常量)
	double dval = 3.14;
	const int temp = dval;
  	double *p3 = &dval;
  	const int *p4 = &temp;
  	std::cout << "*p3 = " << *p3 <<", *p4 = " << *p4 << std::endl; //*p3 = 3.14, *p4 = 3
  	*p3 = 1.23;
  	std::cout << "*p3 = " << *p3 <<", *p4 = " << *p4 << std::endl; //*p3 = 1.23, *p4 = 3,改变i/*p3的值无法改变*p4的值
  • 常量指针(即const指针,由定义知,不变的是指针本身的值而非指向的那个值。属于const对象的一种,一旦被定义其值就不能再修改,因此其必须在被定义的同一语句中完成初始化。另外可以通过常量指针或是其他途径修改常量指针指向的那个值)的初始值来源:地址(包括&普通非常量 和 &普通常量,算术类型一致)或 同算术类型指针(包括普通非常量指针、常量指针和指向常量的指针);
	int i1 = 1024; //普通非常量
	const int i2 = 512; //普通常量
	int *p0 = &i1; //普通非常量指针
	const int *p1 = &i2; //指向常量的指针
	
	int *const p2 = &i1; //true,利用&普通非常量(为地址)完成初始化
  	const int *const p3 = &i2; //true,利用&普通常量(为地址)完成初始化
  	int *const p4 = &i2; //false,常量指针p4(int)和&i2地址(const int)类型不一样
  	int *const p5 = p0; //true,利用普通非常量指针完成初始化,反之可以,因为两者类型一致
  	int *const p6 = p5; //true,利用常量指针完成初始化
    const int *const p7 = p1; //true,利用指向常量的指针完成初始化
    int *const p8 = p1; //false,不能用不同类型的指向常量的指针完成初始化
    const int *const p9 = p3; //true,利用指向常量的常量指针完成初始化
    //指向常量的常量指针归为常量指针
2.4 理解逻辑
  • 复合类型初始化理解逻辑

    • 综合判断法:借助const速断法,利用算术类型一致性和名词定义去判断能否初始化;
    • 原理判断法:利用算术类型一致性和名词定义去判断能否初始化;
    • const速断法:借助顶层const 和 底层const判断(具体定义见《C++ Primer》,第57面),一般而言,顶/无/底 ← 顶/无 和 底 ← 底 都可以初始化的,而顶/无 ← 底 都不可以初始化的,个别对象初始化判断有误,因此只能是一种辅助或过渡判断方法;
    • 顶层const对象:被定义后其值就不能再改变,因此必须在其被定义的同一条语句内完成初始化;
    • 普通非常量:数据类型为算术类型的非常量,普通非常量初始化后还可以赋值;
    • 普通常量:数据类型为算术类型的常量,普通常量初始化后不可以赋值;
    • 普通引用:必须初始化,可以通过普通引用修改其所绑定对象的值;
    • 常量引用:对常量的引用不能用于修改其所绑定的对象,但其所绑定的对象是可以被其他途径修改的;
    • 普通指针:,即普通非常量指针,可以修改指向的对象以及指向对象的值;
    • 指向常量的指针:可以修改指向的对象但不能通过该指针修改指向对象的值,不过该指向对象的值可以通过其他途径修改;
    • 常量指针:不能修改指向的对象,但可以通过常量指针或是其他途径修改常量指针指向的那个值;
    • 引用的类型必须与其所绑定对象的类型相一致,除非是对常量的引用/常量引用;指针的类型必须与其所指向对象的类型相一致,除非是对指针的引用(不是常量指针);
    • 要想绑定到普通常量/指向普通常量,必须用绑定到常量的引用或指向常量的指针,但是绑定到常量的引用或指向常量的指针不一定要绑定到普通常量/指向普通常量。
    • 算术类型转换运算是算术类型对象(包括普通常量和普通非常量)独有的,比如复合类型就没有类型转换运算。另外,可以用不同算术类型的普通常量给普通非常量初始化,也可以用不同算术类型的普通非常量给普通常量初始化。
  • 常见类型转换

    • 算术类型内部转换:int - float - double - char - bool(包括带符号型、无符号型、X整型、X浮点型、扩展字符型和各种前后缀);
    • 无限定词到有限定词的转换:int → const int等,反之不行;(这种就是前面称的“算术类型广义一致”)
  • 引用类型和算术类型初始值来源图:
    在这里插入图片描述

    • 普通常量

      • 普通常量 ← 普通常量:顶 ← 顶,true,算术类型一致;
      • 普通常量 ← 普通非常量:顶 ← 无,true,算术类型转换/会忽略掉顶层的const,算术类型不一致,但不影响赋值;
      • 普通常量 ← 普通引用:顶 ← 无,true,算术类型转换/会忽略掉顶层的const,算术类型不一致,但不影响赋值;
      • 普通常量 ← 常量引用:顶 ← 底,true,从算术类型一致上理解(按const判断法有误);
      • 普通常量 ← 字面值常量:顶 ← 无,true,算术类型转换/会忽略掉顶层的const,算术类型不一致,但不影响赋值;
    • 普通非常量

      • 普通非常量← 普通非常量:无 ← 无,true,算术类型一致;
      • 普通非常量 ← 普通常量:无 ← 顶,true,会忽略掉顶层的const,因此算术类型虽不一致,但不影响;
      • 普通非常量 ← 普通引用:无 ← 无,true,算术类型一致;
      • 普通非常量 ← 常量引用:无 ← 底,true,从算术类型不一致,但不影响赋值上理解(按const判断法有误);
      • 普通非常量 ← 字面值表达式:无 ← 无,true,算术类型一致;
    • 普通引用

      • 普通引用 ← 普通引用:无 ← 无,true,算术类型一致;
      • 普通引用 ← 普通非常量:无 ← 无,true,算术类型一致;
      • 普通引用 ← 普通常量:无 ← 顶,false,从普通引用定义上理解(按const判断法有误);
      • 普通引用 ← 字面值常量:无 ← 顶,false,从普通引用定义上理解(按const判断法有误);
      • 普通引用 ← 常量引用:无 ← 底,false,从普通引用定义上理解;
    • 常量引用

      • 常量引用 ← 常量引用:底 ← 底,true,算术类型一致;
      • 常量引用 ← 普通常量:底 ← 顶,true,算术类型一致;
      • 常量引用 ← 普通非常量:底 ← 无,true,算术类型虽不一致,但不影响绑定;
      • 常量引用 ← 普通引用:底 ← 无,true,算术类型虽不一致,但不影响绑定;
      • 常量引用 ← 字面值常量:底 ← 无,true,算术类型虽不一致,但不影响绑定;
  • 指针类型初始值来源图:
    在这里插入图片描述

    • 普通指针

      • 普通指针 ← 普通指针:无 ← 无,true,算术类型一致;
      • 普通指针 ← &普通非常量:无 ← 无,true,算术类型一致;
      • 普通指针 ← &普通常量:无 ← 底,false,算术类型不一致;
      • 普通指针 ← 指向常量的指针:无 ← 底,false,算术类型不一致;
      • 普通指针 ← 常量指针:无 ← 顶/底,true/false,其中,true-算术类型一致,false-算术类型不一致;
    • 指向常量的指针

      • 指向常量的指针 ← 指向常量的指针:底 ← 底,true,算术类型一致;
      • 指向常量的指针 ← &普通非常量:底 ← 无,true,算术类型不一致,但不影响指向;
      • 指向常量的指针 ← &普通常量:底 ← 底,true,算术类型一致;
      • 指向常量的指针 ← 普通指针:底 ← 无,true,算术类型不一致,但不影响指向;
      • 指向常量的指针 ← 常量指针:底 ← 顶/底,true,算术类型转换/算术类型一致;
    • 常量指针

      • 常量指针 ← 常量指针:顶/底 ← 顶,true,算术类型一致/算术类型转换;
      • 常量指针 ← 常量指针:顶/底 ← 底,false/true,其中,false-算术类型不一致,true-算术类型一致;
      • 常量指针 ← &普通非常量:顶/底 ← 无,true,算术类型一致/算术类型转换;
      • 常量指针 ← &普通常量:顶/底 ← 底,false/true,false-算术类型不一致,true-算术类型一致;
      • 常量指针 ← 普通指针:顶/底 ← 无,true,算术类型一致/算术类型转换;
      • 常量指针 ← 指向常量的指针:顶/底 ← 底,false/true,其中,false-算术类型不一致,true-算术类型一致;
2.5 补充说明
  • &(取地址符/引用声明符) 和 *(解引用符/指针声明符)
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值