变量和基本类型
自动转化
-
混用有符号类型和无符号类型时,有符号类型会自动转换成无符号类型。其中给无符号类型赋值一个超出它表示范围的值时,结果是初始值对无符号类型表示数值取模后所得的余数
-
unsigned u = 1; int i = -1; cout << i + u; // 输出4294967295
-
变量声明和定义的关系
-
声明使得名字为程序所知
-
声明一个变量使用extern,而且不要显示地初始化变量
-
extern int i; // 声明i
-
包含了显示初始化的声明即称为定义
-
extern double pi = 3.14; // 定义pi
-
-
定义负责创建与名字关联的实体
-
int j; // 定义j
-
变量可以重复声明,但只能定义一次
-
类型检查
C++是一种静态类型语言,其含义是在编译阶段检查类型。其中检查类型的过程称为类型检查。
对象的类型决定了对象所能参与的运算。在C++语言中,编译器会检查数据类型是否支持要执行的运算,如果试图执行类型不支持的运算,编译器将报错并不生成可执行文件。
程序越复杂,静态类型检查越有助于发现问题。然而,前提是编译器必须知道每一个实体对象的类型,这就要求我们在使用某个变量之前必须声明其类型。
引用
- 即左值引用,引用不是对象,没有实际地址,相反的,它只是为一个已经存在的对象所起的另一个名字。
int a = 1024;
int &b = a; // b指向a(是a的另一个名字)
int &c; // 报错:引用必须被初始化
- 为引用赋值,实际上把值赋给与引用绑定的对象
- 获取引用的值,实际上获取与引用绑定的对象的值
- 引用只能绑定在对象上,不能绑定字面值或某个表达式的计算结果
- 引用必须初始化
指针
- 指针本身就是一个对象
int *p1, *p2; // p1、p2是指向int型对象的指针
double dp, *dp2; // dp2是指向double型对象的指针,dp是double型变量
int val = 42;
int *p = &val; // p存放变量val的地址,p是指向变量val的指针
int *p2 = p; // p2存放指针p的地址,p2是指向指针p的指针
cout << *p; // 输出42,p存放val的地址,*p表示取val的值
&和*
int i = 42;
int &r = i; // &紧随类型名出现,是声明的一部分,r是一个引用
int *p; // *紧随类型名出现,是声明的一部分,p是一个指针
p = &i; // &出现在表达式中,是一个取地址符
*p = i; // *出现在表达式中,是一个解引用符
int &r2 = *p; // &是声明的一部分,*是一个解引用符
空指针
int *p1 = nullptr;
int *p2 = 0; // p1、p2等价 都是初始化为空指针
int *p3 = NULL; // 不建议使用
- 建议初始化所有指针
void*指针
-
特殊指针类型,可以存放任意类型对象的地址
-
不能直接操作void*指针所指向的对象
指向指针的指针
- **表示指向指针的指针
- ***表示指向指针的指针指针
int i = 42;
int *pi = &i;
int **ppi = π
指向指针的引用
- 引用本身不是对象,不能定义指向引用的指针
- 指针本身是对象,可以定义指针的引用
int i = 42;
int *p;
int *&r = p; //r是一个第指针p的引用
常量引用
- 初始化常量引用时允许绑定一个非常量的对象。
- 常量引用可以指向非常量,但不允许通过常量引用改变非常量的值。
int i = 42;
const int &r1 = i;
const int &r2 = 42;
const int &r3 = r1 * 2;
int &r4 = r1 * 2; // 报错:r4是非常量引用
int i = 42;
int &r1 = i;
const int &r2 = i; // 正确,但是不能通过r2修改i的值
r1 = 0; // 正确,i被修改为0
r2 = 0; // 报错:r2是一个常量引用
指向常量的指针
-
允许一个指向常量的指针指向一个非常量对象
-
指针可以改变指向的地址,但不能通过该指针改变地址的值
const int i = 42;
int *p = &i; // 报错:p是一个普通指针
const int *p2 = &i;
*p2 = 0; // 报错,不能给*p2赋值
int k = 5;
p = &k; // 正确,但不能通过指针p改变k的值
常量指针
-
把指针本身定义为常量
-
常量指针必须初始化,一旦初始化不能再更改
int errNum = 0;
int *const curErr = &errNum; // 指针curErr一直指向errNum
const double pi = 3.14;
const double *const ptr = π // 指向常量的常量指针
-
顶层const表示指针本身是一个常量(常量指针)
-
底层const表示指针所指向的对象是一个常量(指向常量的指针)
-
顶层const可以表示任意的对象是常量
-
底层const与指针和引用等复合类型有关
常量表达式
constexpr
-
常量表达式的值需要在编译时就得到计算
-
认定一个变量是常量表达式,将其声明为constexpr类型
-
C++11规定,运行将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。
-
声明为constexpr的变量一定是常量,而且必须用常量表达式初始化。
constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size(); // 只有当size是一个constexpr函数时才正确
const int* p1 = nullptr; // p1是一个指向整型常量的指针
constexpr int* p2 = nullptr; // p2是一个指向整数的常量指针
处理类型
类型别名
// 方式1
typedef double wages;
// 方式2(C++11)
using SI = Sales_item;
typedef char *pstring; // pstring表示指向char的指针类型
pstring ptr; // 指向char的指针
const pstring cstr = 0; // 指向char的常量指针
const pstring *ps; // 指针,指向的对象为指向char的常量指针
auto
- auto会忽略顶层const,保留底层const
int i = 0;
const int ci = i;
auto b = ci; // 等价与int b = 0;(忽略定增const)
auto d = &i; // 等价于int *d = &i;
auto e = &ci; // 等价于const int* e = &ci;(保留底层const)
- 希望auto推断出的是一个顶层const,需要明确指出
const auto f = ci; // 等价于 const int f = 0;
decltype
- 选择并返回操作数的数据类型
const int ci = 0, &cj = ci;
decltype(ci) x = 0; // const int x = 0;
decltype(cj) y = x; // const int &y = x;
decltype(cj) z; // 报错:z是一个引用,必须初始化
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; // int b = 42;
decltype(*p) c; // 报错:int &c; 引用必须初始化 对指针进行解引用操作得到指针指向对象的引用
// decltype()的表达式如果是加了括号的变量,结果将是引用
decltype((i)) d; // 报错:int &d; 引用必须初始化
decltype(i) e; // int e;