C++_变量和基本类型
C++ 的基本内置类型分为两大类:
- 算术类型:用于表示数值和字符
- 空类型(void):不对应具体的值,常用于特殊场景,如函数无返回值、
void*指针等
算术类型
算术类型分为:
- 整型:包括字符类型和布尔类型
- 浮点型
常见内置类型
| 类型 | 含义 | 最小尺寸 |
|---|---|---|
bool |
布尔类型 | 未规定 |
char |
字符 | 8 位 |
wchar_t |
宽字符 | 至少 16 位 |
char16_t |
Unicode 字符 | 16 位 |
char32_t |
Unicode 字符 | 32 位 |
short |
短整型 | 16 位 |
int |
整型 | 16 位 |
long |
长整型 | 32 位 |
long long |
长长整型 | 64 位 |
float |
单精度浮点数 | 6 位有效数字 |
double |
双精度浮点数 | 10 位有效数字 |
long double |
扩展精度浮点数 | 10 位有效数字 |
注意:表中的“最小尺寸”是语言标准规定的下限,实际大小依赖编译器和平台。
整型之间的大小关系
C++ 保证:
1 | short <= int <= long <= long long |
也就是说:
int至少和short一样大long至少和int一样大long long至少和long一样大
有符号与无符号
除 bool 和扩展字符类型外,整数类型通常都有:
- 有符号类型:
signed - 无符号类型:
unsigned
例如:
1 | unsigned int a; |
字符类型
字符类型有 3 种:
charsigned charunsigned char
其中:
char不等于signed charchar到底表现为有符号还是无符号,由编译器决定
建议:不要混用有符号类型和无符号类型。
字面值常量
字面值常量就是程序中直接写出的值。
整型字面值
| 字面值 | 含义 |
|---|---|
20 |
十进制 |
024 |
八进制 |
0x14 |
十六进制 |
浮点字面值
例如:
1 | 3.14159 |
特点:
- 可以写成小数形式
- 也可以写成科学计数法
- 指数部分用
e或E - 默认类型是
double
字符和字符串字面值
| 字面值 | 含义 |
|---|---|
'a' |
字符字面值 |
"Hello World!" |
字符串字面值 |
说明:
- 字符字面值表示单个字符
- 字符串字面值本质上是由常量字符组成的数组
- 编译器会自动在字符串末尾添加一个空字符
'\0'
例如:
1 | "abc" |
实际存储为:
1 | 'a' 'b' 'c' '\0' |
所以它的长度是 4,不是 3。
转义序列
| 转义序列 | 含义 |
|---|---|
\n |
换行 |
\t |
横向制表符 |
\v |
纵向制表符 |
\r |
回车 |
\b |
退格 |
\f |
进纸 |
\a |
响铃 |
\\ |
反斜杠 |
\' |
单引号 |
\" |
双引号 |
\? |
问号 |
转义序列在程序中通常作为一个字符处理。
布尔字面值和指针字面值
- 布尔字面值:
true、false - 空指针字面值:
nullptr
变量
变量提供一块有名字的存储空间,程序可以通过变量名访问和操作它。
变量定义
变量定义的一般形式:
1 | 类型说明符 变量名; |
也可以一次定义多个变量:
1 | int sum = 0, value, sold = 0; |
说明:
- 多个变量名之间用逗号分隔
- 语句以分号结束
- 每个变量都具有同一个基本类型,但声明符可以不同
初始化
初始化:变量创建时赋予初始值
赋值:对象创建后,再把新值赋给它
例如:
1 | int a = 10; // 初始化 |
在 C++ 中,初始化和赋值是两个不同的概念。
初始化方式
常见初始化写法:
1 | int a = 0; |
其中:
{}形式称为列表初始化- 列表初始化有助于避免某些隐式类型转换
例如:
1 | int x{3.14}; // 错误,存在精度丢失 |
默认初始化
如果定义变量时没有提供初值,就会发生默认初始化。
内置类型变量的默认初始化规则
定义在函数体之外的变量
自动初始化为0定义在函数体内部的内置类型变量
不会被初始化,值是未定义的
例如:
1 | int global_var; // 值为 0 |
使用未初始化的内置类型变量是危险的。
声明与定义
C++ 支持分离式编译,因此“声明”和“定义”是不同的概念。
区别
- 声明:让名字为程序所知
- 定义:创建与名字关联的实体
extern
如果只想声明变量而不定义,可以使用 extern:
1 | extern int i; // 声明,不定义 |
注意:
- 带初始化的声明就是定义
例如:
1 | extern int k = 10; // 这是定义,不是单纯声明 |
结论:
- 变量只能定义一次
- 变量可以声明多次
标识符
标识符就是程序中名字的统称,比如:
- 变量名
- 函数名
- 类名
- 类型名
命名规则
C++ 标识符:
- 由字母、数字、下划线组成
- 必须以字母或下划线开头
- 区分大小写
- 不能使用关键字
例如:
1 | num |
不合法:
1 | 2abc |
命名建议
- 名字应体现含义
- 变量名通常用小写
- 类名通常首字母大写
- 多单词命名风格应统一,如:
student_namestudentName
作用域
作用域是程序中某个名字有效的区域。
常见作用域
- 全局作用域:定义在所有函数外
- 块作用域:定义在
{}内部
例如:
1 | int global_val = 10; // 全局作用域 |
说明:
- 内层作用域可以隐藏外层同名变量
- 不建议随意定义与全局变量同名的局部变量
复合类型
复合类型是基于其他类型定义出来的类型,常见的有:
- 引用
- 指针
引用
引用是对象的别名。
定义引用
1 | int val = 1024; |
说明:
ref是val的另一个名字- 引用必须初始化
- 引用一旦绑定,就不能再绑定到其他对象
错误示例:
1 | int& r; // 错误,引用必须初始化 |
引用的特点
- 引用不是对象
- 引用本身不占用独立语义上的存储对象身份
- 对引用的操作就是对原对象的操作
例如:
1 | int a = 10; |
指针
指针是“存放地址的对象”。
定义指针
1 | int* p; |
多个变量一起定义时要注意:
1 | int* p1, *p2; // p1 和 p2 都是 int* |
获取对象地址
用取地址符 & 获取对象地址:
1 | int val = 42; |
解引用
用 * 访问指针所指向的对象:
1 | int val = 42; |
只有当指针指向有效对象时,才能解引用。
指针的状态
指针可能处于以下几种状态:
- 指向一个对象
- 指向紧邻对象末尾的下一个位置
- 空指针
- 无效指针
空指针
空指针不指向任何对象。
1 | int* p1 = nullptr; |
推荐使用:
1 | int* p = nullptr; |
nullptr是 C++11 引入的,更安全、更明确。
void* 指针
void* 可以存放任意对象的地址,但不能直接访问对象内容。
1 | void* p = nullptr; |
因为 void* 不知道指向对象的具体类型,所以不能直接解引用。
复合类型的声明
一条定义语句中,基本类型只有一个,但每个声明符可以不同:
1 | int i = 1024, *p = &i, &r = i; |
这里:
i是intp是int*r是int&
指向指针的指针
1 | int val = 1024; |
关系:
1 | p2 -> p1 -> val |
指针的引用
引用不能“指向引用”,但可以“引用一个指针”:
1 | int val = 42; |
这里 r 是 p 的引用。
const 限定符
const 用来表示只读,即对象的值不能被修改。
1 | const int val = 42; |
说明:
const对象必须初始化- 一旦初始化,其值不能修改
const 引用
指向常量的引用通常称为常量引用:
1 | const int val = 42; |
特点:
- 不能通过
r修改val - 普通引用不能绑定到常量对象
1 | int& r2 = val; // 错误 |
补充:常量引用还能绑定到字面值或临时对象:
1 | const int& r = 10; // 合法 |
指针和 const
指向常量的指针
1 | const int* p = &val; |
或:
1 | int const* p = &val; |
含义:
- 可以改
p的指向 - 不能通过
p修改所指对象
常量指针
1 | int* const p = &val; |
含义:
- 不能改
p的指向 - 可以通过
p修改对象(前提是对象本身非常量)
指向常量的常量指针
1 | const int* const p = &val; |
含义:
- 不能改指向
- 也不能改所指对象
顶层 const 与底层 const
- 顶层 const:对象本身是常量
- 底层 const:对象所指向的内容是常量
例如:
1 | int i = 0; |
constexpr 和常量表达式
常量表达式
常量表达式:在编译时就能求值,并且值不会改变的表达式。
例如:
1 | const int a = 10; |
这里 a 和 b 都可能是常量表达式。
但下面不是:
1 | int val = 20; |
constexpr 变量
constexpr 用于声明真正的编译期常量:
1 | constexpr int val = 20; |
要求:
- 必须是常量
- 必须用常量表达式初始化
1 | constexpr int sz = size(); // 只有 size() 是 constexpr 函数时才合法 |
如果确定一个值在编译期已知,优先考虑使用
constexpr。
constexpr 与指针
1 | const int* p = nullptr; // 指向 const int 的指针 |
注意:
constexpr修饰指针时,表示指针本身是常量- 不代表所指对象一定是常量
处理类型
当类型写法复杂时,可以使用类型工具来简化。
类型别名
typedef
1 | typedef double wages; |
这里:
wages是double的别名base是double的别名p是double*的别名
using
1 | using db = double; |
这是 C++11 推荐的写法。
auto
auto 让编译器根据初始值自动推断变量类型:
1 | auto i = 0; // int |
要求:
- 使用
auto时必须初始化
decltype
decltype 用于推断表达式的类型,但不会计算表达式的值。
1 | decltype(f()) sum = x; |
这里 sum 的类型就是函数 f() 的返回类型。
decltype 与引用
如果 decltype 的参数是变量名,则返回该变量的精确类型,包括 const 和引用。
1 | const int val = 42; |
注意:
1 | decltype(ref) z; // 错误,引用必须初始化 |
特别注意
1 | decltype(var) |
和
1 | decltype((var)) |
不同:
decltype(var):如果var不是引用,结果就是变量本身类型decltype((var)):因为(var)是表达式,所以结果通常是引用类型
例如:
1 | int i = 0; |
自定义数据结构
C++ 中可以使用关键字:
structclass
来定义自己的数据类型。
简单理解:
struct和class本质上都可以定义类类型- 主要区别在于默认访问权限不同:
struct默认是publicclass默认是private
例如:
1 | struct Student { |
易错点总结
初始化 ≠ 赋值
1 | int a = 10; // 初始化 |
局部内置变量不会自动初始化
1 | int main() { |
引用必须初始化
1 | int& r; // 错误 |
不要解引用无效指针
1 | int* p = nullptr; |
区分两种 const 指针
1 | const int* p; // 不能通过 p 改值 |
推荐使用 nullptr,而不是 0 或 NULL
1 | int* p = nullptr; |




