2.1 基本内置类型
算数类型
C++的基本数据类型包括算术类型和空类型。
算数类型包括字符、整形数、布尔值和浮点数。
常见的类型和所占位数为:
类型 | 最小尺寸 |
---|---|
bool | 未定义 |
char | 8位 |
short | 16位 |
int | 16位 |
long | 32位 |
long long | 64位 |
float | 6位有效数字,32位 |
double | 10位有效数字,64位 |
long double | 10位有效数字 |
C++语言规定一个int至少和一个short一样大,一个long至少和一个int一样大。(int也可能32位)
整形还可以分为带符号的(signed)和无符号的(unsigned)
int、short、long和long long都是带符号的
字符型被分为三种:char、signed char和unsigned char。
字符型被分为三种,但是表现形式只有两种,char可能有符号,也可能无符号,编译器决定。
类型转换
不要混用带符号数和无符号数
字面值常量
可以将整形字面值写成十进制、八进制、十六进制。
八进制0开头、十六进制0X开头。
负数并不是字面值常量,负号并不在字面值之内,仅仅是对字面值取负数而已。
单引号括起来的一个字符为char型字面值,双引号括起来的零个或多个字符构成字符串型字面值。
true和false是布尔类型的字面值。
nullptr是指针类型字面值。
有些符号不能直接打印,需要用到转义序列。
符号 | 命令 |
---|---|
换行符 | \n |
横向制表符 | \t |
退格符 | \b |
单引号 | \' |
反斜线 | \\ |
问号 | ? |
2.2 变量
变量定义
变量定义的基本形式是:类型说明符,随后紧跟一个或多个变量名组成的列表,同时还可以为变量赋初值。
例如: int sum = 0, value;
当对象在创建时获得一个特定的值,我们说这个对象被初始化了
列表初始化:用花括号来初始化变量
int a = {0};
int b{0};
*当用于内置类型时,如果使用列表初始化且初始值存在丢失风险,编译器将报错。
例如:
long double ld = 3.1415;
int a{ld}, b = {ld}; #错误,存在丢失风险
int c(ld), d= ld; #正确,且丢失了部分值
变量声明和定义关系
一个文件如果想使用别处定义的名字,则必须包含对那个名字的声明。
声明使得名字为程序所知,定义负责创建与名字关联的实体。
变量的声明和定义都规定了类型和名字,但是定义还申请存储空间,也可能为变量赋初值。
如果想声明一个变量而非定义,在变量名前加extern,而且不要显示地初始化变量。
例如:
extern int i; #声明i而非定义i
int j; #声明并定义j
包含显示初始化的声明即称为定义。
extern double pi = 3.14; #定义
标识符
由字母、数字、下划线构成,并且第一个不能是数字。
标识符不能是关键字
变量名一般小写,如index
类名一般首字母大写
多个单词,遵循驼峰原则
作用域
局部作用域和全局作用域
2.3 复合类型
引用
引用就是为对象起了一个别名,声明符写成&d来定义引用类型,d为变量名
引用必须初始化
int ival = 1024;
int &refval = ival;
int &refval2; #报错:引用必须初始化
一般初始化时,初始值拷贝到新建对象中,引用则是和初始值绑定在一起
引用不是对象,只是一个别名
引用必须严格匹配,并且不能绑定字面值。
int &i = 1024; #错误,绑定字面值
double dval = 3.14;
int &refval = dval; #错误,引用类型初始值必须是int类型
指针
指针是指向另外一种类型的复合类型,指针本身就是一个对象,而且无需定义时赋值。
定义指针的方法写成*d,其中d是变量名。
如果一条语句中定义了几个指针变量,每个变量前都要有符号*。
指针存放对象的地址,想要获取地址,需要使用取地址符(操作符&)
int *p1, p2, *p3;
int ival =42;
int *p = &ival;
指针类型要和指向的类型严格匹配
double dval;
double *pd = &dval;
int *pi = pd; #错误,类型不匹配
如果指针指向一个对象,则允许使用解引用符(操作符*)来访问该对象。
对指针解引用会得出所指的对象。
int ival = 42;
int *p = &ival;
cout << *p; #输出42
空指针不指向任何对象,试图使用一个指针之前可以检查他是否为空。
得到空指针最直接的方法就是用字面值nullptr
int *p1 = nullptr;
*建议初始化所有指针,使用未初始化的指针,将出现重大错误。
**void ***指针
可以存放任意对象的地址,但是不知道该地址中是什么类型对象。
所以不能直接操作void *指针类型对象,只能和其他指针比较,作为函数输入输出等。
复合类型声明
变量的定义包括一个基本数据类型和一组声明符,在同一条定义语句中,基本数据类型只有一个,声明符形式却可以不同。
int i = 1024, *p = &i, &r = i;
i是一个int型的数,p是一个int型指针,r是一个int型引用
int* p1, p2; //p1是指向int的指针,p2是int
指向指针的指针
通过*区分指针的级别,**表示指向指针的指针。
int ival = 1024;
int *pi = &ival; //pi指向一个int型的数
int **ppi = &pi ; //ppi指向一个int型指针
指向指针的引用
引用本身不是对象,所以不存在指向引用的指针,但指针是对象,所以存在指向指针的引用。
int i = 42;
int *p;
int *&r=p; //r是对指针p的引用
r = &i; //r引用指针,所以是p指向i
*r = 0; //解引用得到i,也就是p指向对象,i变成0
要理解r的类型是什么,最简单的方法就是从右往左读,最先遇到&符号,所以是一个引用,然后遇到*,说明引用了一个指针,然后遇到int,指出引用的是一个int型指针。
2.4 const限定符
const可以定义常量,任何试图对常量赋值的行为都会报错。
const int bufSize = 512;
bufSize = 256; //错误,试图向const对象写值
初始化和const
如果利用一个对象去初始化另一个对象,它们是不是const都无关紧要。
默认状态下,const对象仅在文件内有效
编译器在编译过程中,会把所有常量替换成对应的值,所以const对象仅在文件内有效
如果想在其他文件内也有效,需要加上extern关键字
file_1.cc
extern const int bufSize = 256;
file_1.h
extern const int bufSize; //与file_1.cc中定义的bufSize是同一个
const的引用
把引用绑定在const对象上,称为对常量的引用。
const int ci = 1024;
const int &r1 = ci; //正确
r1 = 42; //错误,不能对常量赋值
int &r2 = ci; //错误,非常量引用绑定常量对象
可以将常量引用绑定到变量上
int i = 42;
const int &r1 = i; //正确
不可以将变量引用绑定到常量上
int &r4 = r1; //错误,r4是一个普通的非常量引用
一般引用的类型与被引用的需要一致,但是初始化常量引用允许是任意表达式,只要能转换成引用的类型即可。
double dval = 3.14;
const int &ri = dval; //正确
这里ri实际上绑定了一个临时量对象,相当于:
const int temp = dval;
const int &ri = temp;
const引用绑定一个变量时,不能通过const对象改变值,但是通过其他引用改值允许。
int i = 42;
int &r1 = i;
const int &r2 = i;
r1 = 0; //正确
r2 = 0; //错误,常量引用不能赋值
指针和const
指向常量的指针不能用于改变其所属对象的值。要想存放常量对象的地址,只能使用常量指针。写法为:*const int ptr
const double pi = 3.14;
double *ptr = & pi; //错误,不是指向常量的指针
const double *cptr = & pi; //正确,指向常量的指针
cptr = 42; //错误,不能赋值
指针的类型必须和所指向的类型一致,但是也可以指向非常量
double dval = 3.14;
cptr = &dval; //正确
指向常量的指针和对常量的引用一样,都可以指向或者绑定变量,只是不能通过它们去改变值而已,但是可以通过其他指向或者绑定的变量去改变值。
指向常量的指针不能改变常量,但是可以指向其他常量。
const指针
指针本身为常量,称为常量指针。常量指针必须初始化,而且一旦初始化完成,它的值就再也不能改变。写法为:*int const ptr
int errNumb = 0;
int *const curErr = &errNumb; //curErr将一直指向errNumb
const double pi = 3.14;
const double *const pip = & pi; //pip是一个指向常量的常量指针
常量指针指向的对象不能改变,但是可以改变指向对象的值。
*curErr = 0;
顶层const
顶层const表示指针本身是个常量。
底层const表示指针所指的对象是个常量。
更一般的,顶层const可以表示任意对象本身是常量,底层const表示被指向的是常量。
int i = 0;
int *const p1 = &i; //顶层const
const int ci = 42; //顶层const
const int *p2 = &ci; //底层const
const int *const p3 = p2; //左侧底层const,右侧顶层const
cosnt int &r = ci; //引用都是底层const
当拷贝时,顶层const不受干扰,但是底层const必须具有相同的底层const资格
int *p = p3; //错误,p3有底层const,p没有
p2 = p3; //正确,p2,p3都有底层const,顶层const可以忽略
p2 = &i; //正确,int*能转换成const int *
int &r = ci; //错误,普通引用不能绑定常量上
const int &r2 = i; //正确,const 引用可以绑定到普通变量上
constexpt和常量表达式
常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。
一个对象是不是常量表达式由数据类型和初始值共同决定。
const int max_files = 20; //max_files是常量表达式
const int limit = max_files + 1; //limit是常量表达式
int staff_size = 27; //staff_size不是常量表达式
const itn sz = get_size(); //sz不是常量表达式
constexpr变量
c++11新标准规定,允许将变量声明为constexpr类型,以便编译器验证是否是常量表达式。声明为constexpr的变量一定是常量,而且必须用常量表达式初始化。
constexpr int mf = 20; //20是常量表达式
constexpr int limit = mf + 1; //mf + 1是常量表达式
constexpr int sz = size(); //只有当size函数是constexpr函数,才是正确声明语句
字面值类型
算数类型、引用、指针都是字面值类型。
自定义类、IO库、String类型都不是字面值类型。
指针和constexpr
在constexpr声明中定义了一个指针,限定符constexpr仅对指针有效,与指针所指对象无关。
const int *p = nullptr; //p是指向整型常量的指针
constexpr int *q = nullptr; //q是指向整形的常量指针
constexpr把他定义的对象置为顶层const。
constexpr指针既可以指向常量,也可以指向非常量。
2.5 处理类型
类型别名
类型别名是给类型起一个新的名字。有两种方法可以定义类型别名。
传统方法使用typedef
typedef double wages; //wages是double的同义词
typedef wages base, *p; //base是double的同义词,p是double* 的同义词
C++11规定了一种新方法,使用别名声明来定义
using SI = double; //SI是double的同义词
wages hourly,weekly; //等价于doubel hourly,weekly;
SI hourly,weekly; //等价于doubel hourly,weekly;
指针、常量和类型别名
这里有问题,好还理解