2.1 基本内置类型
包括
- 算术类型
- 空类型(void)
2.1.1 算术类型
- 整型
包括了char和bool - 浮点型
算术类型所占的大小在不同机器上有所差别
类型 | h含义 | 最小尺寸(字节) |
---|---|---|
bool | 未定义 | |
char | 1 | |
wchar_t | 宽字符 | 2 |
char16_t | Unicode字符 | 2 |
char32_t | Unicode字符 | 4 |
short | 2 | |
int | 2 | |
long | 4 | |
long long | 8 | |
float | 6位有效数字 | |
double | 10位有效数字 | |
long double | 10位有效数字 |
带符号类型和无符号类型
- singed
- unsinged
只能表示大于等于0的数
对于char(注意)
- char
具体为有符号还是无符号由编译器决定 - singed char
- unsigned char
类型选择的建议
- 非负选用unsigned
- 若数值超过了int的范围,选用long long, 因为long一般情况下和int大小一样
- char不要用于算术运算, 因为它在有些机器上是有符号的,一些是无符号的
- 浮点运算用double, float精度不够,且通常float和double的计算代价相差不大
2.1.2 类型转换
- bool 非bool类型
- true 1
- false 0
- 非bool类型 bool
- 0 false
- 非0 true
- 赋给无符号数超出范围的值
结果为该值对能表示的最大值取模后的余数 - 赋给有符号数超出范围的值
结果是未知的
含有无符号类型的表达式
- 同时含有无符号类型和有符号类型的时候,有符号类型会转换成无符号类型
- 从无符号数中减去一个数,必须保证结果非负
-
无符号数做for循环计数变量也会出现问题
当u为0时,--u得到的是取模后的余数,为正数,而不会是-1
for(unsigned int u = 10;u >= 10;--u)
cout << u << endl;
//改为
while(u > 0)
{
u--;//必须先减再输出
cout << u << endl;
}
2.1.3 字面值常量
每个字面值常量都对应一种数据类型,字面值常量的形式和值决定了它的数据类型
整型和浮点型字面值
整型
- 进制问题
- 20---十进制
- 024---八进制(0开头)
- 0x14---十六进制(0x开头)
- 符号问题
- 十进制字面值是带符号数
- 八进制和十六进制数字面值可能是带符号数也可能是无符号的
- 类型问题
- 十进制字面值为int, long, long long中能容下它的最小的一个
- 八进制和十六进制字面值为int, unsigned int, long, unsigned long, long long, unsigned long long中能容纳其数值的最小的一个
- short没有字面值
浮点型
- 浮点型字面值是double
字符和字符串字面值
-
字符字面值
‘a’
-
字符串字面值
"abc"
转义序列
常用的有
- \n换行
- \t制表符
指定字面值的类型
字符和字符串字面值前缀
前缀 | 含义 | 类型 |
---|---|---|
u | Unicode16字符 | char16_t |
U | Unicode32字符 | char32_t |
L | 宽字符 | wchar_t |
u8 | UTF-8字符串(用于字符串) | char |
整型浮点型字面值后缀
后缀 | 含义 | 类型 |
---|---|---|
u or U | unsigned能匹配的最小类型 | 无符号 |
l or L | 至少为long | long |
ll or LL | 至少为long long | long long |
f or F | float | float |
l or L(浮点型) | long double | long double |
布尔字面值和指针字面值
布尔
- true
- false
指针
- nullptr
2.2 变量
2.2.1 变量定义
初始值
double price = 109.99;
列表初始化(C++11)
//等价于 int sold = 0;
int sold = {0};
int sold{0};
int sold(0);
默认初始化
-
内置类型的全局变量
初始化为0
-
内置类型的局部变量
不被初始化
2.2.2 变量声明和定义的关系
为了支持分离式编译(拆分为多个文件), C++将声明和定义区分开来。要在多个文件里使用同一个变量,必须将声明和定义分离。
声明(Declaration)
使得名字为程序所知(可以声明别处定义过的变量,进行使用)
定义(Definition)
负责创建与名字关联的实体
extern int i; //只声明而不定义i
int j; //声明并且定义j
extern double pi = 3.14 //extern语句如果包含初始值就不再只是声明,包括了定义(函数体内部这么做会报错)
2.2.3 标识符
- 不能为保留字
- 必须以下划线或者字母开头
- 不能连续出现两个下划线
- 不能下划线紧接着大写字母开头
- 定义在函数体外的标识符不能以下划线开头
2.2.4 名字的作用域
-
定义于所有花括号之外的为全局作用域
在整个程序范围内都可以使用
-
定义在花括号内拥有块作用域
只在该花括号内有效
-
嵌套的作用域
int a = 1; void func1() { int a = 2; cout << a << endl; //输出2,内层覆盖外层作用域 cout << ::a << endl; //输出1,使用域操作符来使用全局变量 }
2.3 复合类型
复合类型指基于其他类型定义的类型。
2.3.1 引用
引用为对象其了另外一个名字。使用&d来定义引用类型
int a = 100;
int &ref = a;//ref 指向 a
//int &ref;错误,引用必须被初始化
引用必须被初始化(绑定)
无法令引用重新绑定到另外一个对象
引用本身不是一个对象, 不能定义引用的引用
引用的类型必须与其绑定的对象严格匹配
-
引用只能绑定在对象上,不能绑定在字面值上
int &ref = 10;//错误
2.3.2 指针
指针是指向另外一种类型的复合类型,通过*d来定义
int a = 1;
int *pointer = &a;
指针与引用的区别
- 指针本身是一个对象
- 指针可以先后指向不同的对象
- 指针不需要赋初值
- 由于引用不是对象, 所以不能定义指向引用的指针
获取对象的地址
通过取地址运算符&
指针也需要与其被指对象的类型严格匹配(有两个例外的情况)
int a = 1;
int *pointer = &a;//取地址运算符&
空指针与野指针
-
空指针即为nullptr,不指向任何对象
int *p1 = nullptr;//为C++11新标准 int *p2 = 0;//也是空指针 int *p3 = NULL;//NULL是一个预处理变量,不属于命名空间std,由预处理器负责管理,可以直接使用
- 野指针即未被初始化的指针(指向未知地址)
利用指针访问对象
使用解引用符*
int a = 1;
int *pointer = &a;//取地址运算符&
cout << *pointer << endl;
其他指针操作
-
非空指针可以转化为true, 空指针转化为false
int *p1 = nullptr; if(p1) { cout << "p1 is not nullptr" << endl; }
-
指针比较
通过==来比较两个指针(两个指针都为空,也是相等)
void*指针
可以存放任意类型对象的地址,但是在使用时必须进行类型转换才能使用
int a = 1;
void *p1 = &a;//合法
cout << *p1 << endl;//不合法
int *p2 = (int *)p1;
cout << *p1 << endl;//合法
2.3.3 理解复合类型的声明
变量的声明
- 由基本数据类型 + 声明符组成,声明符中可以加上类型修饰符
int *p;
int a;
int &ref = a;
// *, &在这里为类型修饰符
指向指针的指针
通过*的个数来区别指针的级别
int a = 1;
int *pi = &a;//一级指针
int **pii = π//二级指针
//也是由于使用了不同的类型修饰符
绑定指针的引用
由于指针是对象,所以可以用引用绑定指针
int i = 42;
int *p = &i;
int *&ref = p;//绑定int *指针的引用
2.4 const限定符
const限定符用来声明常量值
- const变量一旦声明不可改变值
- const变量必须赋初值
const int bufsize = 512;
const作用域
默认情况下,const对象仅在文件内有效
若想要让const对象在其他文件中也能访问到,必须在定义和声明时都添加extern关键字
extern const int bufsize = 125;//定义也要加上extern
extern const int bufsize;//声明加上extern
2.4.1 const的引用
可以将const对象绑定到引用上, 但是该引用必须也用const修饰
const int a = 125;
const int &ref = a;
初始化和对const的引用
在两种情况下,引用绑定的对象可以不和引用类型匹配
- 常量引用绑定的对象能够转化为引用的类型
- 允许一个常量引用(注意必须是被const修饰的引用)绑定非常量对象
int i = 42;//非常量对象
const int &ref1 = i;//绑定非常量对象,注意必须为const
double dval = 3.14;//浮点数
const int &ref2 = dval;//绑定类型不匹配的对象,但是浮点数可以转化为int
/*
实际上发生的事情为,创建了一个临时变量,该引用绑定到了该临时变量上
如果该引用不是const的,修改该引用修改的是临时变量,而不是原来的变量,所以必须为const的
const int temp = dval;
const int &ref2 = temp;
*/
2.4.2 指针和const
类似于常量引用,一样可以定义指向常量的指针,但是指针必须用const修饰
const double pi = 3.14;
double *ptr = π //错误,必须用const修饰
const double *ptr = π
const指针
代表的意思为不能改变该指针所指向的对象.注意const的位置
int *const pointer;
const指针必须初始化
int a = 0;
int b = 0;
const int *ptr = &a;
ptr = &b; //错误
2.4.3 顶层const
顶层const
对于指针来说表示指针本身是个常量
一般的,顶层const可以表示任意的对象是常量
底层const
对于指针来说表示指针所指的对象是个常量
一般的,底层const则与指针和引用等符合类型的基本类型部分有关(所指的东西)
2.4.4 constexpr和常量表达式
常量表达式指不会改变,且在编译过程中就能计算得到的表达式
- 字面值是常量表达式
- 常量表达式初始化的const对象也是常量表达式
constexpr常量
c++11新标准规定, 可以用constexpr来声明一个常量表达式变量(是一个常量)
constexpr int mf = 20; //是常量表达式
constexpr int m1 = size();//不是常量表达式,不行
字面值类型
- 自定义类, IO库, string类型不属于字面值类型不能定义为constexpr
- 指针类型变量要定义为constexpr必须满足
- 要么被初始化为nullptr
- 要么是存储于某个固定地址中的对象
- 定义于函数体内的变量一般来说不是存储在固定的地址
- 定义于所有函数体外的对象地址固定不变
指针和constexpr
用constexpr修饰指针,表达的意思使该指针为常量指针(即该指针本身不能改变)
2.5 类型别名
使用类型别名可以简化类型书写
2.5.1 类型别名
typedef
typedef double wages; //wages i = 0.0; 即为声明一个double类型的变量
typedef int *p;//p为int *的同义词
typedef int p[10];//p为int arr[10]的同义词
别名声明(c++11)
using SI = Sales_item; //SI是Sales_item的同义词
指针、常量和类型别名
typedef int *p;//p为int *的同义词
const p i = 0; //i为指向char的常量指针(注意不是指向常量 的指针)
不能简单的将类型别名替换为他原来的名字
const int *i = 0;//这就变成了指向const int常量的指针了,而不是常量指针
2.5.2 auto类型说明符
C++11新标准允许用auto类型说明符让编译器替我们去分析表达式所属的类型
int i1 = 0;
float i2 = 1.2;
auto var = i1 + i2;//var为float
复合类型、常量和auto
编译器推断出来的类型和初始值类型有时不完全一样,编译器会做适当的调整
-
使用引用来初始化时,会使用被引用的对象的类型作为auto的类型
int i = 0, &r = i; auto a = r;//a的类型为int 而不是 int &
-
auto会忽略顶层const, 保留底层const
const int ci = i, &cr = ci; auto b = ci;//b为int, 忽略了顶层const auto c = cr;//c为int, cr为引用这时候要看所引用对象,而const int ci为顶层const所以忽略const auto d = &i;//d为int * auto e = &ci;//e是const int * ???????这里不怎么理解
如果希望推断出auto类型为顶层const,可以加上const
const auto f = ci;
还可以将引用类型设置为auto
auto &g = ci;