《C++Primer》第 5 版第 2 章笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/CV2017/article/details/82660703

变量与基本类型

数据类型的意义:告诉我们数据的意义以及我们能在数据上执行的操作

一. 基本内置类型

1. 两大种类:算术类型、空类型

算术类型可分为:整型、浮点型

整型可分为:int、字符、布尔类型

2. char 1字节 8位,short int = short 2字节 16位,int 4 字节 32位,long int = long 4字节 32位,long long 8字节 64位 C++11新定义的,float 4字节 32位,double 8字节 64位

一般情况下,类型 float 和 double 分别有 7 和 16 个有效位

bool 类型的取值是 true 或 false

Unicode 是用于表示所有自然语言中字符的标准

3. 可寻址的最小内存块称为字节,存储的基本单位称为字,通常由几个字节组成

4. 除去布尔型和扩展的字符型外,其他整型可以划分为带符号的和无符号的,带符号类型可以表示正数、负数或 0 ,无符号类型则仅能表示大于等于 0 的值

5. 类型 int 、short 、long 和 long long 都是带符号的,在类型名前加 unsigned 得到无符号类型,比如 unsigned long,unsigned int 可以缩写为 unsigned

6. 与其他整型不同,字符型被分为了三种:char、signed char 和 unsigned char,表现形式只有两种(带符号的和无符号的),unsigned char 为 0 至 255,signed char 为 -128 至 127

7. 选择类型的经验准则

  • 当明确知晓数值不可能为负时,选用无符号类型
  • 超过 int 的范围,整数类型选用 long long
  • 在算术表达式中不要使用 char 或 bool,只有在存放字符或布尔值时才使用它们,类型 char 在一些机器上是有符号的,在另一些机器上是无符号的,使用 char 进行运算容易出问题,明确指定 signed char 或 unsigned char
  • 执行浮点数运算用 double

8. 取模运算也称为取余运算,unsigned char c = -1,则实际结果为 255,实际结果是该值对 256 取模后所得的余数

9. 使用非布尔值作为条件,那么他会被自动地转换为布尔值,类似于把非布尔值赋给布尔变量时的操作完全一样,i 的所有其他取值(非 0)都将使条件为 true

int i = 42; //如果 i 的值为 0,则条件的值为 false
if(i) //if 的条件值将为 true
{
    i = 0;
}

10. 用算术运算符和括号将运算对象(也称操作数)连接起来的、符合C++语法规则的式子,称C++算术表达式

11. 一个算术表达式里既有无符号数又有 int 值时,那个 int 值会被转换为无符号数

12. 不能混用带符号类型和无符号类型,带符号数会自动转换为无符号数

13. 0 开头表示八进制数,0x 开头表示十六进制数,十进制数是带符号数,但是十进制字面值不是负数

14. 浮点数字面值有科学计数法表示 3.14159 = 3.14159E0

15. 'a' 字符字面值,"Hello World" 字符串字面值,字符串字面值的类型实际上是由常量字符构成的数组,编译器在每个字符串的结尾添加了一个空字符('\0'),字符串字面值的实际长度比它的内容多 1,分多行书写的字符串字面值,实际上是一个整体

cout << "a really, really long string literal "
		"that spans two lines" << endl;

16. 变量提供一个具名的、可供程序操作的存储空间

17. iostream 和 string 都是在命名空间 std 中定义的

18. string 是一种表示可变长字符序列的数据类型

19. 对象是指一块能存储数据并具有某种类型的存储空间,在同一条语句中对象定义完就可以使用了

int sum = 0, sum = sum + 7;

20. 初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代

21. 使用列表初始化内置类型的变量可能存在丢失信息的风险

//4中形式初始化变量
int iA = 0;
int iA = {0};
int iA{0};
int iA(0);

long double ld = 3.1415926546;
int a{ld}, b = {ld}; //错误:转换未执行,因为存在丢失信息的风险
int c(ld), d = ld; //正确,转换执行,且确实丢失了部分值

22. 定义变量时没有指定初值,则变量被默认初始化

  • 内置类型的变量未被显式初始化,其值由定义的位置决定,定义于任何函数体之外的变量被初始化为 0,定义在函数体内部的内置类型变量如果没被初始化,其值是未定义的,如果试图拷贝或以其他形式访问此类值将引发错误
  • 绝大多数类无须显式初始化定义对象,这样的类提供一个合适的默认值,而一些类要求每个对象都被显式初始化,如果创建了一个该类的对象而未对其做明确的初始化操作,将引发错误
string empty; //empty 非显示地初始化为一个空串

23. string 类型的变量接收无参数的初始化方式,不论变量定义在函数内还是函数外都被默认初始化为空串

24. 建议初始化每一个内置类型的变量

25.变量声明和定义的关系:

  • C++ 语言支持分离式编译,该机制允许将程序分割为若干个文件,每个文件可被独立编译,需要有在文件间共享代码的方法
  • 一个文件使用别处定义的名字则必须包含对那个名字的声明,而定义负责创建与名字关联的实体
  • 变量声明规定了变量的类型和名字,定义与之相同,定义还申请存储空间,也可能会为变量赋一个初始值
  • 声明一个变量而非定义它,在变量名前添加关键字 extern,而且不要显式地初始化变量,任何包含了显式初始化的声明即成为定义
  • 在函数体内部,如果试图初始化一个由 extern 关键字标记的变量,将引发错误
  • 变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义
  • 变量只能被定义一次,但是可以多次声明
extern int i; //声明 i 而非定义 i
int j; //声明并定义 j 
extern double pi = 3.1416; //定义

26. C++ 是一种静态类型语言,在编译阶段检查类型

27. C++ 标识符概念规则:

  • 由字母、数字和下画线组成,其中以字母或下画线开头,对大小写敏感
  • 关键字名字不能用作标识符
  • 用户自定义的标识符不能连续出现两个下画线,也不能以下画线紧连大写字母开头
  • 定义在函数体外的标识符不能以下画线开头

28. 变量命名规范:

  • 标识符要体现实际含义
  • 变量名一般用小写字母
  • 用户自定义的类名一般以大写字母开头
  • 标识符由多个单词组成,则单词间应该有区分

29. 名字的作用域:

  • 同一个名字出现在程序的不同位置,也可能指向的是不同实体
  • 大部分作用域都以花括号分隔
  • 名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束
  • 名字 main 定义于所有花括号之外,和大多数定义在函数体之外的名字一样拥有全局作用域,在整个程序的范围内都可使用
  • 建议第一次使用变量时再定义它
  • 嵌套作用域,作用域一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字,允许内层重新定义外层作用域的名字
  • 因为全局作用域本身没有名字,当作用域左侧为空时,向全局作用域发出请求获取作用域操作符右侧名字对应的变量
  • 如果函数有可能用到全局变量,不宜再定义一个同名的局部变量

30. 复合类型:是指基于其他类型定义的类型,常用的有指针和引用

31. 一条声明语句 = 一个基本数据类型 + 一个声明符列表组成,通常声明符就是变量名

32. 通常说的引用指的是左值引用,引用为对象起了另外一个名字,引用类型引用另外一种类型,将声明符写成 &d 的形式来定义引用类型,其中 d 是声明的变量名,引用必须初始化

int ival = 1024;
int &refVal = ival; //refVal 指向 ival(是 ival 的另一个名字)
int &refVal2; //报错,引用必须初始化

refVal = 2; //把 2 赋给 refVal 指向的对象,此处即是赋给了 ival
int ii = refVal; //与 ii = ival 执行结果一样

int &refVal3 = refVal; //refVal3 绑定到了那个与 refVal 绑定的对象上,这里就是绑定到 ival 上
int i = refVal; //利用与 refVal 绑定的对象值初始化变量 i

33. 在初始化变量时,初始值会被拷贝到新建的对象中,定义引用时,程序将引用和它的初始值绑定在一起,而不是将初始值拷贝给引用,无法令引用重新绑定到另外一个对象,之后每次使用这个引用都是访问它最初绑定的那个对象,引用并非对象,只是为一个已经存在的对象所起的另外一个名字,不能定义引用的引用

34. 允许在一条语句中定义多个引用,其中每个标识符都必须以符号&开头

int i = 1024, i2 = 2048; // i 和 i2 都是 int
int &r = i, r2 = i2; // r 是一个引用,与 i 绑定在一起,r2 是 int
int i3 = 1024, &ri = i3; 
int &r3 = i3, &r4 = i2;

35. 引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起,

int &refVal4 = 10; //错误:引用类型的初始值必须是一个对象
double dval = 3.14;
int &refVal5 = dval; //错误:此处引用类型的初始值必须是 int 对象,类型要匹配

36. 指针特点:

  • 指向另外一种类型的复合类型
  • 与引用类似,实现对其他对象的间接访问
  • 指针本身是一个对象,允许对指针赋值和拷贝
  • 指针的生命周期内可以先后指向几个不同的对象
  • 指针无须在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针如果没有初始化,也将拥有一个不确定的值

37. 在一条语句中定义了几个指针变量,每个变量前都必须有符号 *

int *ip1, *ip2; //ip1、ip2 都是指向 int 型对象的指针
double dp, *dp2; //dp 是 double 型对象,dp2 是指向 double 型对象的指针

38. 指针存放某个对象的地址,要想获取该地址,需要使用取地址符,因为引用不是对象,没有实际地址,所以不能定义指向引用的指针

int iVal = 42;
int* p = &iVal; //p存放变量 iVal 的地址,或者说 p 是指向变量 iVal 的指针

39. 指针的类型要和它所指向的对象严格匹配

double dval;
double* pd = &dval; //正确,初始值是 double 型对象的地址
double* pd2 = pd; //正确:初始值是指向 double 对象的指针

int* pi = pd; //错误:指针 pi 的类型和 pd 的类型不匹配
pi = &dval; //错误:试图把 double 型对象的地址赋给 int 型指针

40. 指针的值(即地址)应属于下列 4 种状态之一:

  • 指向一个对象
  • 指向紧邻对象所占空间的下一个位置   理解参考
  • 空指针,意味着指针没有指向任何对象
  • 无效指针,也就是上述情况之外的其他值

41. 使用解引用符 * 来访问对象,为 *p 赋值实际上是为 p 所指的对象赋值

int ival = 42;
int* p = &ival;
cout << *p;

42. 试图拷贝或以访问无效指针的值都将引发错误,因此程序员必须清楚任意给定的指针是否有效,解引用操作仅适用于那些确实指向了某个对象的有效指针

43. 符号的上下文决定了符号的意义

44. 空指针不指向任何对象,在试图检查一个指针之前代码可以检查它是否为空,生成空指针的办法:

int* p1 = nullptr; //常用这个
int* p2 = 0;
//需要首先 #include cstdlib
int* p3 = NULL; 避免使用这个

int zero = 0;
p1 = zero; //错误:不能直接把 int 变量直接赋给指针

45. 建议初始化所有指针

int i = 42;
int* p = 0;
int* pi2 = &i;
int* pi3;

pi3 = pi2; //pi3 和 pi2 指向同一个对象 i
pi2 = 0;
*pi2 = 0; //i 的值被改变,指针 pi2 并没有改变

46. 赋值永远改变的是等号左侧的对象

47.  指针可用在条件表达式中,指针的值是 0 ,条件取 false,任何非 0 指针对应的条件值都是 true

int i = 42;
int* p = 0;
int* p2 = &i;
if(p) //p 的值是 0,因此条件为 false
if (p2) //p2 指向 i,因此它的值不是 0,条件的值是 true

48. 对于两个类型相同的指针,可用 == 或 != 判断,比较的结果是布尔类型

49.两个指针存放的地址值相同(两个指针相等)有三种可能:

  • 他们都为空
  • 都指向同一个对象
  • 都指向了同一个对象的下一地址

注意:一个指针指向某对象,同时另一个指针指向另外对象的下一地址,此时可能出现这两个指针值相同的情况,实例代码如下

#include <iostream>
using namespace std;

int a[] = {1,2};

int main()
{
	int* p1 = &a[1]; //指针 p1 指向对象a[1]
	int* p2 = &a[0] + 1; //指针 p2 指向对象a[0]的下一个
	if (p1 == p2)
	{
		cout << "p1(" << p1 << ") = p2(" << p2 << ")\n"; 
	}
	return 0;
}

50. void* 是一种特殊的指针类型,可存放任意对象的地址,不能直接操作 void* 指针所指的对象

51. 一条定义语句可能定义出不同类型的变量,* 和 &是类型修饰符

int i = 1024, *p = &i, &r = i;
int* p1, p2; //p1是指向int的指针,p2是int

52. 指针是对象,存在对指针的引用,从右向左阅读确定变量的真实含义

int i = 42;
int* p = nullptr;
int* &r = p; //r是一个对指针p的引用
r = &i;
*r = 0;

53. const 对象一旦创建后其值就不能再改变,所以 const 对象必须初始化,初始值可以是任意浮躁的表达式

const int i = get_size();
const int j = 42;
const int k; //错误

54. 不改变 const 对象的操作中有一种是初始化,利用一个对象去初始化另外一个对象,是不是 const 都无关紧要,拷贝一个对象的值并不会改变它,一旦拷贝完成,新的对象就和原来的对象没什么关系了

int i = 42;
const int ci = i; //正确:i 的值被拷贝给了 ci
int j = ci; //正确:ci 的值被拷贝给了 j

55. 当多个文件中出现了同名的 const 变量,其实等同于在不同文件中分别定义了独立的变量,在 const 变量不管是声明还是定义都添加 extern 关键字,这样只需要定义一次就可以了,如果想在多个文件之间共享 const 对象,必须在变量的定义之前添加 extern 关键字

//file1.cc 定义并初始化了一个常量,该常量能被其他文件访问
extern const int bufsize = fun();
//file1.h
extern const int bufsize; //与 file1.cc 中定义的 bufsize 是同一个,指明 bufsize 并非本文件独有,它的定义将在别处出现

56. 将引用绑定到 const 对象上,不能修改,引用的类型必须与所引用对象的类型一致

const int ci = 1024;
const int &r1 = ci; //正确:引用及其对应的对象都是常量
r1 = 42; //错误:r1是对常量的引用
int &r2 = ci; //错误:试图让一个非常量引用指向一个常量对象

57. 允许将 const int& 绑定到一个普通的 int 对象上,对 const 的引用可能引用一个并非 const 的对象,常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未作限定

int i = 42;
int &r1 = i;
const int &r2 = i; //r2 也绑定对象 i,但是不允许通过 r2 修改 i 的值
r1 = 0; 
r2 = 0;

58. 指向常量的指针,指针的类型必须与其所指对象的类型一致,允许一个指向常量的指针指向一个非常量对象,指向常量的指针也没有要求规定其所指的对象必须是一个常量,所谓指向常量的指针仅仅要求不能通过该指针改变对象的值

const double pi = 3.14; 
double* ptr = &pi; //错误:ptr 是一个普通指针
const double *cptr = &pi; //正确
*cptr = 42; //错误

59. const 指针,不变的是指针本身的值而非指向的那个值

  • 非常量引用不能引用字面值常量 0

60. 顶层 const 表示指针本身是个常量,底层 const 表示指针所指的对象是一个常量,拓展来说,顶层 const 可以表示任意的对象是常量,对任何数据类型都适用,如算术类型、类、指针。底层 const 与指针和引用等复合类型的基本类型部分相关,指针类型既可以是顶层 const 也可以是底层 const,用于声明引用的 const 都是底层 const

61. 执行对象的拷贝操作时,拷入与拷出的对象必须具有相同的底层 const 资格,非常量可以转换成常量

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

const int max_files = 20;
const int limit = max_files + 1;
int staff_size = 27; //不属于常量表达式
const int sz = get_size(); //不属于常量表达式,因为具体值直到运行时才能获取到

63. 声明为 constexpr 类型的变量一定是一个常量,而且必须用常量表达式初始化,常量表达式的值需要在编译时就得到计算,算术类型、引用和指针都属于字面值类型,自定义类、Sales_item、IO库、string 类型不属于字面值类型,也就不能被定义成 constexpr,一个 constexpr 指针的初始值必须是 nullptr 或者 0,或者是存储于某个固定地址中的对象,函数体内的变量不被定义成  constexpr,定义在所有函数体外之外的对象其地址固定不变,能用来初始化  constexpr 指针

64. 在  constexpr 声明中如果定义了一个指针,限定符  constexpr 仅对指针有效,与指针所指的对象无关,constexpr 把它所定义的对象置为了顶层 const

const int *p = nullptr; //p 是一个指向整型常量的指针
constexpr int *q = nullptr; //q 是一个指向整数的常量指针,constexpr 把它所定义的对象置为了顶层 const

65. 定义类型别名的两种方法:传统方法是使用关键字 typedef 和 新方法是使用别名声明 using 来定义类型的别名

typedef double wages; //wages 是 double 的同义词
typedef wages base, *p; //base 是 double 的同义词,p 是 double* 的同义词

using SI = Sales_item; //SI 是 Sales_item 的同义词

66. 类型别名不能简单完全同等替换

typedef char* pstring;
const pstring cstr = 0; //cstr 是指向 char 的常量指针,基本数据类型是指针
const pstring *ps; //ps 是一个指针,它的对象是指向 char 的常量指针

67. auto 类型说明符,用它就能让编译器替我们去分析表达式所属的类型,auto 让编译器通过初始值来推算变量的类型,以引用的类型,auto 定义的变量必须有初始值,一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型都必须一样:

auto i = 0, *p = &i; //正确:i 是整数、p 是整型指针
auto sz = 0, pi = 3.14; //错误,sz 和 pi 的类型不一致

68. auto 会忽略顶层 const,同时底层 const 则会保留下来

const auto f = ci; //ci 的类型是 int,f 是 const int

auto &h = 42; //错误,不能为非常量引用绑定字面值

69. 第二种类型说明符 decltype 作用是选择并返回操作数的数据类型,编译器分析表达式并得到他的类型,却不实际计算表达式的值

70. 如果 decltype 使用的表达式是一个变量,decltype 返回该变量的类型(包括顶层 const 和引用在内),如果 decltype 使用的表达式不是一个变量,则 decltype 返回表达式结果对应的类型引用,表达式的内容是解引用操作,则 decltype 将得到引用类型,decltype(*p) 的结果类型是 int&,而非 int

71. decltype((variable)) 的结果永远是引用,而  decltype(variable) 结果只有当 variable 本身就是一个引用才是引用

72. 赋值是会产生引用的一类典型表达式,引用的类型就是左值的类型,如果 i 是 int,则表达式 i = x 的类型是 int&

73. 自定义的数据结构:类,类体右侧的表示结束的花括号后必须写一个分号,这是因为类体后面可以紧跟变量名以示对该类型对象的定义,所以分号必不可少

struct Sales_data
{

};
Sales_data accum, trans, *salesptr;

74. 类的数据成员定义了类的对象的具体内容,每个对象有自己的一份数据成员拷贝,修改一个对象的数据成员,不会影响其他 Sales_data 的对象

75. 类一般都不定义在函数体内,通常定义在头文件中,类所在头文件名字应与类的名字一样

76. 确保头文件多次包含仍能安全工作的常用技术是预处理器,头文件保护符,#ifdef 当且仅当变量已定义时为真,#ifndef 当且仅当变量未定义时为真,一旦检查结果为真,则执行后续操作直至遇到 #endif 指令为止,预处理变量的名字全部大写

猜你喜欢

转载自blog.csdn.net/CV2017/article/details/82660703