变量和基本类型(C++Primer 5th)
字符型
分为三种:char、signed char 和 unsigned char 。char 和 signed char并不一样,但字符的表现形式只有两种:带符号的和无符号的。
有符号与无符号
注意: unsigned 类型和 int 类型的加法等运算。unsigned为负数时会出现异常,自动转换为无符号数。
对象
通常情况下,对象是指一块能存储数据并具有某种类型的内存空间。
初始化
初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。
作用域
预处理变量无视C++中作用域的规则。
extern
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显示地初始化变量。
任何包含了显示初始化的声明即成为定义,如:extern double pi = 3.1416 ; //定义。
在函数体内部,如果试图初始化一个由extern关键字标记的变量,将引发错误。
标识符
用户自定义的标识符中不能连续出现两个下划线,也不能以下划线紧连大写字母开头。此外,定义在函数体外的标识符不能以下划线开头。
int _ ;(这个是正确的)
引用
严格来说,当使用术语“引用(reference)”时,指的是左值引用(C++11引入了右值引用)。引用为对象起了另一个名字,定义引用时,程序将引用和初始值绑定在一起,而不是将初始值拷贝给引用。
引用类型必须初始化,初始值必须是个对象而非数值,注意要同类型。错误示范:int &refVal =10;
指针
指针和引用的对比:1、指针本身就是一个对象。2、指针无须在定义时赋初值。
C++11标准下,空指针最好使用nullptr,同时尽量避免使用NULL。
不能直接操作 void* 指针所指的对象。
int ival = 42 ; // 指向必须同类型
int *p = &ival; // p存放变量ival的地址,或说p是指向变量ival的指针。
int **pi = &p; // pi指向一个int型的指针,表示指向指针的指针
int *p = nullptr ; // 等价于int *p =0 ;
int* p1,p2 ; // 注意p1为指针,p2为int变量
int *&r = p; // r是一个对指针p的引用
const
const对象必须初始化。默认情况下,const对象被设定为仅在文件内有效。
顶层const(top-level const)表示指针本身是个常量,底层const(low-level const)表示指针所指的对象是一个常量。
// const引用
const int ci = 1024;
const int &r1 = ci; // 正确引用,引用及其对象都是常量
int &r2 = ci; // 错误引用,试图让非常量引用指向一个常量对象
int &r = 0 ; // 错误
const int &r = 0; // 正确
// const指针
int errNumb =0;
int *const curErr = &errNumb; // curErr将一直指向errNumb,可以通过*curErr修改errNumb的值
const double pi = 3.14159;
const double *const pip = π // pip是一个指向常量对象的常量指针,不能通过*pip修改pi的值
const int *p; // 合法,但p没有指向任何实际的对象
// 顶层const OR 底层const
int i =0;
int *const p1 = &i; // 顶层const,p1的值不能改变
const int ci = 42; // 顶层const,ci的值不能改变
const int *p2 = &ci; // 底层const,p2的值允许改变
const int &r = ci; // 底层const,用于声明引用的const都是底层const
constexpr
C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。
constexpr int mf = 20; // 20是常量表达式
constexpr int limit = mf + 1; // mf + 1 是常量表达式
constexpr int sz = size(); // 仅size()是constexpr函数时才正确
constexpr const int *p = &i; // p是常量指针,指向整型变量i
constexpr int *p1 = &j; // p1是常量指针,指向整数j
类型别名
传统的方法是使用关键字 typedef,新标准使用别名声明即关键字using来定义别名。
typedef double wages; // wages是double的别名
typedef wages base,*p; // base是double的别名,p是double*的别名
using SI = Sales_items; // SI是Sales_item的别名
auto
C++新标准引入了auto类型说明符,auto让编译器通过初始值来推算变量的类型,因此,auto定义的变量必须有初始值。
一条声明语句中,所有变量的初始基本数据类型必须保持一致。
auto一般会忽略掉顶层const,同时保留底层const,如对常量对象取地址是一种底层const。
auto item = val1+val2; // 初始化
auto i = 0, *p = &i; // 正确:i是整数,p是整型指针
auto sz =0, pi = 3.14; // 错误:sz和pi的类型不一致
auto c = &ci; // c是一个指向整数常量的指针,底层const
const auto f = ci; // 顶层const
auto &n =i,*p2 = &ci; // 错误:i 是 int,而 &ci 是 const int
decltype
作用是选择并返回操作数的数据类型,不计算表达式的值(与auto不同),意思是,从表达式类型推断要定义的变量类型,同时又不希望用该表达式的值初始化变量。
需要指出的是,引用从来都作为其所指对象的同义词,只有用在decltype处是个例外。
decltype的表达式如果是加上了括号的变量,结果将是引用。
decltype(f()) sum = x; // sum的类型就是函数f的返回类型
const int ci = 0,&cj = ci;
decltype(ci) x = 0; // x的类型是const int
decltype(cj) y = x; // y的类型是const int&,y是引用,必须初始化
decltype(cj+0) a; // 正确,类型为int,r为表达式r+0的一部分
int i = 42;
decltype((i)) b; // 错误,b为引用,即int&,必须初始化
decltype(i) c; // 正确,c为一个未初始化的int
auto和decltype的区别主要有三个方面:
第一,auto类型说明符用编译器计算变量的初始值来推断其类型,而decltype虽然也让编译器分析表达式并得到它的类型,但是不实际计算表达式的值。
第二,编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。例如, auto —般会忽略掉顶层const,而把底层const保留下来。与之相反,decltype会保留变量的顶层const。
第三,与auto不同,decltype的结果类型与表达式形式密切相关,如果变量名加上了一对括号,则得到的类型与不加括号时会有不同。如果decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,则编译器将推断得到引用类型。