C++基础总结:变量和基本类型
文章目录
一、基本内置类型
C++基本数据类型为算数类型和空类型(void)。算数类型包含字符、整数型、浮点型和布尔值。
1. 算数类型
算数类型分为整型(包括字符和布尔值)和浮点型。
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 未定义 |
char | 字符 | 8位 |
wchar_t | 宽字符 | 16位 |
char16_t | Unicode字符 | 16位 |
char32_t | Unicode字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 长整型 | 32位 |
long long | 长整型 | 62位 |
float | 单精度浮点型 | 6位有效数字 |
double | 双精度浮点型 | 10位有效数字 |
long double | 扩展精度浮点型 | 10位有效数字 |
基本字符类型是char,一个char的大小和一个机器字节一样,大多数机器字节由8比特构成。
*注:可寻址的最小内存块称为“字节(byte)”,存储的基本单元称位“字(word)”。一个字节由8bit构成,字则由32或64 bit构成,也就是4或8 byte。
带符号类型和无符号类型
除布尔型和扩展的字符型之外,其他整型可以分为带符号(signed) 和 无符号(unsigned)。
对于int、short、long和long long类型,都是带符号的,其signed标识符可省略,在类型前加unsigned,可使其成为无符号类型。unsigned int可省略为unsigned。
与上面类型不同的是,字符型被分为三种:char、signed char和unsigned char,实际为signed char和unsigned char。char表现为signed char和unsigned char中的一种,具体由编译器决定。
2. 类型转换
类型所能表示的值的范围决定了转换的过程
- 非布尔类型转换为布尔类型:0为false,否则为true;
- 布尔值赋给非布尔类型:false为0,否则为1;
- 浮点数赋给整数型:仅保留小数点之前的部分;
- 整数值赋给浮点值:小数部分记为0;
- 赋给无符号类型超过范围的值:初始值对无符号类型表示数值总数取模后的余数;
- 赋给带符号类型超过范围的值:结果是未定义。
3. 字面值常量
字面值可以通过字面形式看出其数据类型,用于将值按照其具体数据类型保存到内存。例如20是int类型,20L是long类型。
整型和浮点型字面值
整型字面值可以写作十进制、八进制和十六进制形式:
- 默认为十进制: 20
- 以0开头的整数为八进制:024
- 以0x或0X开头为十六进制:0x14
浮点型字面值表现为小数或以科学计数法表示的指数,其中指数部分用E或e标识:
3.14159 3.14159E0 0. 0e0 .001
字符和字符串字面值
单引号括起来的一个字符称为char型字面值,双引号括起来的0个或多个字符则构成字符串的字面值。
转义序列
字符 | 含义 | 字符 | 含义 | 字符 | 含义 |
---|---|---|---|---|---|
\n | 换行符 | \t | 横向制表符 | \a | 报警符 |
\v | 纵向制表符 | \b | 退格符 | \" | 双引号 |
\\ | 反斜线 | \? | 问号 | \’ | 单引号 |
\r | 回车符 | \f | 进纸符 |
指定字面值的类型
字符和字符串字面值
前缀 | 含义 | 类型 |
---|---|---|
u | Unicode16字符 | char16_t |
U | Unicode32字符 | char32_t |
L | 宽字符 | wchar_t |
u8 | UTF-8(仅用于字符串字面常量) | char |
整型字面值
后缀 | 最小匹配类型 |
---|---|
u or U | unsigned |
l or L | long |
ll or LL | long long |
浮点型字面值
后缀 | 类型 |
---|---|
f or F | float |
l or L | long double |
例:
L’a’ //宽字符型字面值,类型是wchar_t
u8"hi!" //utf-8字符串字面值(utf-8用8位编码一个Unicode字符)
42ULL //无符号整型字面值,类型是 unsigned long long
1E-3F //单精度浮点型字面值,类型是float
3.14159L //扩展精度浮点型字面值,类型是long double
布尔字面值和指针字面值
布尔类型:true和false是布尔类型字面值
指针:nullptr是指针字面值
二、变量
1. 变量定义
对象是指一块能存储数据并具有某种类型的内存空间。
初始化不是赋值:初始化时创建变量时赋予其一个初始值,而赋值是把对象的当前值擦除,以一个新值替代。
列表初始化
- 使用花括号初始化变量的方式称作列表初始化
- 内置类型使用列表初始化的特点:如果使用列表初始化存在丢失信息的风险,则编译器将报错。
默认初始化
- 如果定义变量时没有指定初始值,则变量被默认初始化
- 如果内置类型的变量没有显式初始化,它的值由定义的位置决定
- 定义于任何函数体之外的变量被初始化为0
- 定义在函数体内部的内置类型变量将不被初始化,一个未被初始化的内置类型变量的值是未定义的,如果试图拷贝或访问,将会报错。建议初始化每个内置类型的变量。
- 类决定其初始化对象的方式,是否允许不经初始化就定义对象也由类决定,同时可以决定初始值
2. 变量声明和定义的关系
- 变量声明规定了变量的类型和名字,定义还申请了存储空间,也可能会为变量赋一个初始值。
- 如果想申明一个变量而非定义它,就在变量名前添加关键字extern,并且不要显式地初始化变量
例:extern int i; //声明i而非定义i
int j; //声明并定义j
*变量能且只能被定义一次,但可以被多次声明 - 变量的定义必须出现在且只能出现在一个文件中,而其它用到该变量的文件必须对其进行声明,绝对不能重复定义。
三、复合类型
1. 引用
- 引用是为对象起另外一个名字,因不能给引用重新绑定另一对象,所以引用必须初始化。
- 因为引用不是对象,所以不能定义引用的引用。
2. 指针
- 指针是指向另一类型的符合类型。
- 指针本身就是一个对象,允许对指针赋值和拷贝
- 指针无须再定义时赋初值,和其它内置类型一样,在块作用域内定义的指针如果没有初始化,将拥有一个不确定的值
- 指针的四个状态:
1 指向一个对象
2 指向紧邻对象所占空间的下一个位置
3 空指针,意味着指针没有指向任何对象
4 无效指针,也就是上述情况之外的其它值 - 如果拷贝或访问无效指针都将引发错误
- void*指针
- void*是特殊的指针类型,可用于存放任意的对象类
- 输出void*指向的值,可以先转换为对应类型的指针,再取值。例:*(int*)p
四、const限定符
- const int i = get_size() //正确:运行时初始化
const int j = 42; //正确:编译时初始化
const int k; //错误:k是未经初始化的常量 - 编译时初始化时,编译器将在编译过程中把用到该变量的地方都替换成对应的值
- 为避免对同一个变量的重复定义,默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
1. const引用
- const引用可以绑定到一个普通对象或常量对象,但不能通过const引用修改其值
- const引用可以绑定非常量的对象、字面值,甚至是一个表达式
- 当const引用绑定另一个类型对象时:
double dval = 3.14;
const int& ri = dval;
其实实际出现的转换为:
const int temp = dval;
const int& ri = temp;
所以,修改dval的值后,ri的值仍为3
2. 指针和const
指针和const组合,会出现三种形式:
const int *p; //p是一个指针,指向int类型,不能改变指针p指向对象的值。p也叫指针常量
int* const p; //指针p指向int类型对象,可以改变int对象的值,但是不能改变指针p的指向。p也叫常量指针
const int* const p; //指针p指向int类型对象,不能改变int对象的值,也不能改变指针p的指向
3. constexpr和常量表达式
- 常量表达式是指值不会改变,并且在编译过程就能得到计算结果的表达式。所以,字面值和用常量表达式初始化的const对象也是常量表达式。
- 判断一个变量是不是常量表达式,可以通过它的值是否是在运行时计算所得或者在运行时是否会改变来判断。
- constexpr变量
- 可以将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式
- 一般情况下,如果认定变量是一个常量表达式,那就把它声明成constexpr
- 字面值类型
- 显而易见的值,就把它成为“字面值类型”
- 算数类型、引用和指针都是字面值类型
- 虽然指针和引用都能定义成constexpr,但是constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象
- 指针和constexpr
- constexpr限定符仅对指针有效,与指针所指对象无关
const int* p = nullptr; //p是指向整形常量的指针
constexpr int* p = nullptr; //p是指向整数的常量指针 - constexpr指针既可以指向常量,也可以指向非常量
constexpr int* np = nullptr;
int j = 0;
constexpr int i = 42;
//i和j都必须定义在函数体外
constexpr const int* p = &i; //p是常量指针,指向整型常量i
constexpr int* p1 = &j; //p1是常量指针,指向整数j
- constexpr限定符仅对指针有效,与指针所指对象无关
五. 处理类型
1. 类型别名
-
有两种方法可以定义类型别名:typedef和using
- typedef
typedef double wages; //wages是double同义词
typedef wages base, *p; //base是double的同义词,p是double*的同义词 - using
using SI = Sales_item; //SI是Sales_time的同义词
- typedef
-
指针、常量和类型别名
typedef char* pstring;
const pstring cstr = 0; //cstr是指向char的常量指针,所以不能修改指针,但可以修改指针指向的值。所以它等价的是char* const cstr = 0,而不是const char* cstr = 0。
const pstring* ps; //ps是指向char常量指针的指针,所以不能修改它指向的指针的指向,但可以修改ps的指向和指向指针对象指向的值
2. auto类型说明符
- auto让编译器通过初始值来推算变量的类型,所以,auto定义的变量必须有初始值
- auto一般会忽略掉顶层const,同时底层const则会保留下来,比如初始值是指向常量的指针时:
int i = 0;
const int ci = i, &cr = ci;
auto b = ci; //b是一个整数
auto c = cr; //c是一个整数
auto d = &i; //d是一个指向整型的指针
auto e = &ci; //e是一个指向整数常量的指针 - 如果希望推断出的类型有顶层const,需要明确指出
const auto f = ci; - 或者将引用类型设置为auto
auto &g = ci;
auto &h = 42; //错误:不能为非常量引用绑定字面值
const auto &j = 42; //正确:可以为常量引用绑定字面值
3. decltype类型指示符
- decltype可以实现从表达式的类型推断出要定义的变量的类型,而不使用其值初始化变量
decltype(f()) sum = x; //sum的类型就是函数f的返回类型
编译器在推断类型时,并不实际调用,而仅仅使用返回值类型 - decltype和auto处理顶层const和引用的方式有一点不同:
如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)
const int ci = 0, &cj = ci;
decltype(ci) x = 0; //x的类型是const int
decltype(cj) y = x; //y的类型是const int&, y绑定到x
decltype(cj) z; //错误:z是引用,所以必须初始化
4. decltype和引用
- 如果表达式的内容是解引用操作,则decltype将得到引用类型
int i = 42, *p = &i;
decltype(*p) c; //错误:c是int&,必须初始化 - 如果decltype的表达式加上一层或多层括号,结果将是引用
decltype((i)) d; //错误:d是int&,必须初始化
decltype(i) e; //正确:e是一个未初始化的int
总结:decltype((variable))有双层括号,结果永远是引用,而decltype(variable)结果只有当variable本身是一个引用时才是引用