2.1.1 算术类型
1.带符号类型和无符号类型
signed:带符号,表示正数、负数、0
unsigned:无符号的,仅表示大于等于0的值
注意:类型unsigned int可以缩写为unsigned,8比特的unsigned char表示
0~255的值,8比特的signed int表示-127~127,大多数现代计算机将实际的表示范
围定位-128~127。
2.1.2 类型转换
1. 含有无符号类型的表达式
unsigned u=10;
int i=-42;
std::cout<<i+i<<std::endl; //输出-84
std::cout<<u+i<<std::endl; //如果int占32位,输出4294967264
u+i: 2^32-42+10=4294967264
2.2.2 变量声明和定义的关系
如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显示地初始化变量
extern int i; //声明i而非定义i
int j; //声明并定义j
任何包含了显示初始化的声明即成为定义。extern语句如果包含初始值就不再是声明,而变成定义了
extern double pi=3.14; //定义
注意:变量能且只能被定义一次,但是可以被多次声明
2.2.4 名字的作用域
1. 嵌套的作用域
#include<iostream>
int reused = 42;
int main() {
int unique = 0; //unique拥有块作用域
//1、使用全局变量reused;输出42 0
std::cout << reused << " " << unique << std::endl;
int reused = 0; //新建局部变量reused,覆盖了全局变量reused
//2、使用局部变量reused;输出0 0
std::cout << reused << " " << unique << std::endl;
//3、显式访问全局变量reused;输出42 0
std::cout << ::reused << " " << unique << std::endl;
}
注意:如果函数有可能用到某全局变量,则不宜再定义一个同名的局部变量
2.3.1 引用
int ival=1024;
int &refVal=ival; //refVal是ival另一个名字
int &refVal2; //error,引用必须初始化
double dval=3.14;
int &re=dval; //引用类型的初始化必须是int
注意:1、引用必须初始化
2、引用只能绑定在对象上,而不能绑定在字面值或者某个表达式的计算结果上,引用不是对象,所以不能定义引用的引用
2.3.2 指针
如果一条语句中定义了几个指针变量,每个变量前面都必须有符号*:
int *p1,*p2; //p1和p2都是指向int的指针
double dp1,*dp2; //dp1是double型对象,dp2是指向double对象的指针
1. 空指针
三种定义空指针的方法:
int *p1=nullptr;
int *p2=0;
//需要首先#include cstdlib
int *p3=NULL;
//把int变量直接赋值给指针是错误的操作,即使int的值恰好为0也不行
int zero=0;
int *p1=zero;
2. void*指针
void*是一种特殊的指针类型,可以存放任意对象的地址
double obj=3.14,**pd=&obj;
void *pv=&obj;
pv=pd;
2.3.3 理解复合类型的声明
1.指向指针的引用
int i=42;
int *p; //p是一个int型指针
int *&r=p; //r是一个对指针p的引用
r=&i; //r引用了一个指针,因此给r赋值&i就是令p指向i
*r=0; //解引用r得到i,也就是p指向的对象,将i的值改成0
注意:理解r的类型的方法:从右向左阅读r的定义。离变量名最近的符号(此例中是&r的符号&)对变量的类型有最直接的影响,因此r是一个引用。声明符的其余部分用以确定r引用的类型是什么,此例的*说明r引用的是一个指针,最后,声明的基本数据类型部分指出r引用的是一个int类型。
2.4 const限定符(掌握的有点乱)
const一旦创建后其值就不能再改变,所以const对象必须初始化
const int i=42;
const int k; //k未经初始化
1. 初始化和const
如果利用一个对象去初始化另外一个对象,则它们是不是const都无关紧要:
int i=42;
const int ci=i; //正确:i的值被拷贝给了ci
int j=ci; //正确:ci的值被拷贝给了j
注意:拷贝一个对象的值并不会改变它,一旦拷贝完成,新的对象和原来的对象没关系
2. 默认状态下,const对象仅在文件内有效
如果想在多个文件之间共享const对象,必须在变量的定义前添加extern:
//file_1.cpp,初始化一个常量,可以被其他文件访问
extern const int buf=fcn();
//file_1.h
extern const int buf;
2.4.1 const的引用
与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象:
const int ci=42;
const int &r1=ci; //正确:引用及其对应的对象都是常量
int &r2=ci; //错误:让一个非常量引用指向一个常量对象
1. 初始化和对const的引用
引用类型必须与所引用对象类型一致,但有例外。在初始化常量引用是允许用任意表达式作为初始值,只要能转化成引用的类型即可。允许为一个常量引用绑定非常量的对象、字面值,甚至是表达式:
int i=42;
const int &r1=i; //允许将const int&绑定到一个普通int
const int &r2=42; //正确:r2是一个常量引用,字面值
const int &r3=r1*2; //正确:r3是一个常量引用,表达式
int &r4=r1*2; //错误:r4是一个普通的非常量引用
2.4.2 指针和const
与引用一样,可以令指针指向常量或者非常量,类似于常量引用,指向常量的指针不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针:
const double pi=3.14;
double *ptr=π //错误:ptr是一个普通指针
注意:和常量引用一样,指向常量的指针也没有规定其所指的对象必须是一个常量。所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其他方式改变。
1. const指针
常量指针必须初始化,一旦初始化,其值即存放指针的地址不能改变。把*放在const前说明指针是一个常量,即不变的是指针本身的值而不是指向的那个值:
int a1=0;
int *const p1=&a1; //p1一直指向a1
const double pi=3.14;
cosnt double *const pip=π //pip是一个指向常量对象的常量指针,pip指的对象还是pip自己储存的地址都不能改变
2.4.4 constexpr和常量表达式
常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式
const int max=20; //max是常量表达式
const int a1=max+1; //a1是常量表达式
int a2=27; //a2不是常量表达式,只是普通int
const int a3=get_size(); //a3不是常量表达式,具体值在运行时才能获得
1. constexpr变量
允许将变量声明为constexpr类型以便编译器验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是常量,而且必须用常量表达式初始化:
constexpr int mf=20;
constexpr int limit=mf+1;
constexpr int sz=size(); //只有当size是一个constexpr函数时
//才是一条正确的声明语句
注意:一般,如果认定变量是一个常量表达式,就把它声明成constexpr类型
2. 指针和constexpr
在constexpr声明中如果定义了一个指针,constexpr仅对指针有效,与指针所指的对象无关:
const int *p=nullptr; //p是一个指向整型常量的指针
constexpr int *q=nullptr; //q是一个指向整数的常量指针
constexpr const int *pi=&i; //pi是常量指针,指向整型常量i
2.5.1 类型别名
有两种方法定义类型别名:
第一种:typedef:
typedef double wages; //wages是double的同义词
typedef wages base,*p; //base是double的同义词,p是double*的同义词
第二种:别名声明:
using SI=a1; //SI是a1的同义词
1. 指针、常量和类型别名
下面的声明用到了类型pstring,它实际是类型char*的别名:
typedef char* pstring;
const pstring cstr=0; //cstr是指向char的常量指针
const pstring *ps; //ps是一个指针,它的对象是指向char的常量指针
注意:不能直接将类型别名替换成它本来的样子,const是对给定类型的修饰
2.5.2 auto类型说明符
该语句中所有变量的初始基本数据类型都必须一样:
auto i=0,*p=&i; // 正确:i是整数、p是整型指针
auto sz=0,pi=3.14;//错误:sz和pi的类型不一样
2.5.3 decltype类型指示符
decltype的作用是选择并返回操作数的数据结构:
decltype(f())sum=x; //sum的类型就是函数f的返回类型
如果decltype使用的表达式是一个变量,则返回该变量类型:
const int ci=0,&cj=ci;
decltype(ci)x=0; //x的类型是const int
decltype(cj)y=x; //y的类型是const int&
decltype(cj)z; //错误:z是一个引用,必须初始化
若r是一个引用,则decltype®的结果是引用类型。若想结果类型是r所指的类型,可以把r作为表达式的一部分,这个表达式的结果将是一个具体值而非引用:
int i=42,&r=i;
decltype(r+0)b;
若表达式的内容是解引用,则decltype得到引用类型:
int i=42,*p=&i;
decltype(*p)c; //错误:c是int&,必须初始化
decltype(())会得到引用类型:
int i=42;
decltype((i))d; //错误:d是int&,必须初始化
decltype(i)e; //正确:e是一个未初始化的int
注意:decltype(())的结果永远是引用,而decltype()结果只有当里面的是一个引用时才是引用
2.6.3 编写自己的头文件
1. 预处理器概述
#ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到#endif为止。