关键字
关键字 | 官方解释 | 理解 | 场景 |
---|---|---|---|
volatile | volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。 遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。 | 意为“易变的”,用于保护一些被多个线程共享的变量,防止编译器对其进行优化,每次都从内存读取而不是CPU寄存器。 | 1. 并行设备的硬件寄存器存储器映射的硬件寄存器 2. 中断服务程序中修改的供其它程序检测的变量 3. 多线程应用中被几个任务共享的变量 |
static | static 关键字用于定义 静态变量,它可以应用于局部变量、全局变量以及函数。 | 意为“静态的”,static是被声明为静态类型的变量,存储在静态区(全局区)中,其生命周期为整个程序。如果是静态局部变量,其作用域为一对{}内;如果是静态全局变量,其作用域为当前文件。静态变量如果没有被初始化,则自动初始化为0。此外,static也可以修饰函数,使函数成为内部链接属性,只能在当前文件中调用。 | 1. 需要在函数调用之间保留变量状态的局部变量(静态局部变量) 2. 限制全局变量和函数的作用域在当前文件内,避免命名冲突(静态全局变量/函数) 3. 实现单例模式,保持计数器等 |
extern | extern 关键字用于声明一个变量或函数,它告诉编译器该变量或函数在其他文件中定义,允许在当前文件中引用它们。 | 意为“外部的”,用于声明一个变量或函数是在其他文件(或同一文件的其他位置)中定义的,而不是在当前位置定义。它主要用于实现多文件编译时的全局变量和函数的共享。 | 1. 在一个文件中使用另一个文件中定义的全局变量 2. 在一个文件中调用另一个文件中定义的函数 3. 在头文件中声明全局变量,在源文件中定义,供多个源文件使用 |
const | const 关键字用于定义 常量,它指定一个变量的值不能被修改。 | 意为“常量”、“不变的”。被const修饰的变量其值在初始化后不能被修改。它可以修饰基本类型变量、指针、函数参数和函数返回值,以提供数据完整性和安全性。 | 1. 定义程序中不变的常量,如π、数组大小等 2. 修饰函数参数,防止函数内部修改传入的参数值 3. 修饰指针,表示指针指向的内容不可变,或指针本身不可变 4. 作为函数返回值,表示返回的值不可被修改 |
typedef | typedef 关键字用于为现有的数据类型创建一个新的名称(别名)。 | 意为“类型定义”。它不创建新的类型,而是为已有的数据类型(包括结构体、联合体、枚举、基本类型)提供一个更简洁、更具可读性的别名。这有助于提高代码的可移植性和可维护性。 | 1. 为复杂的类型声明创建别名,如结构体、函数指针 2. 提高代码的可读性,使代码更易理解 3. 提高代码的可移植性,当底层数据类型改变时,只需修改typedef定义即可 4. 减少类型名称的冗长,如为unsigned int定义UINT |
变量的初始化行为
- 全局变量: 在函数外部定义的变量,作用域贯穿整个程序。
- 静态全局变量 : 使用 static 关键字修饰的全局变量,作用域仅限于定义它的文件。
- 局部变量 在函数内部定义的变量,作用域仅限于定义它的函数块。
- 静态局部变量 : 在函数内部使用 static 关键字修饰的变量,它的生命周期贯穿整个程序,但在函数外部不可见。它只在第一次执行到定义时被初始化一次。
- 动态内存分配: 使用 new 和 delete (C++) 或 malloc 和 free (C) 在运行时分配和释放内存。
特征/变量类型 | 全局变量 (Global) | 局部变量 (Local, non-static) | 静态局部变量 (Static Local) | 动态分配内存 (e.g., new /malloc ) |
---|---|---|---|---|
定义位置 | 函数外部 | 函数内部 | 函数内部,带 static 关键字 | 堆 (Heap) 上,通过指针访问 |
存储区域 | 数据段或 BSS 段 | Stack | 数据段或 BSS 段 | Heap |
生命周期 | 整个程序运行期间 | 所在函数块执行期间 (进入时创建,退出时销毁) | 整个程序运行期间 (第一次执行到定义时初始化) | 从分配到显式释放 (delete /free ) |
作用域 | 整个程序(用 extern 声明则可在所有文件中使用) | 定义它的函数块内部 | 定义它的函数块内部 | 任何能访问到指针的地方 |
初始化时机 | 程序启动时 | 进入函数块时 | 第一次执行到定义语句时 | 调用 new 或 malloc 时 |
未显式初始化时的默认值 | **零值 **: - 数值类型:0 - 指针: nullptr /NULL - 布尔: false - 对象:调用默认构造函数 (如果存在) | **垃圾值 / 不确定值 **: - 任何值都可能,不确定。 - 警告:访问是未定义行为! | **零值 **: - 数值类型:0 - 指针: nullptr /NULL - 布尔: false - 对象:调用默认构造函数 (如果存在) | 取决于分配方式: - new Type / malloc : 垃圾值- new Type() / new Type[N]() : 零值 (对于基本类型) 或 调用默认构造函数 (对于类类型) |
默认初始化机制 | 静态初始化 | 自动初始化 | 静态初始化 (只发生一次) | 值初始化 或默认初始化 |
使用static关键字修饰 | 作用域限制在当前文件,避免命名冲突。 | \ | \ | \ |
备注 | 在整个程序中可见且生命周期最长。 | 最常用,生命周期最短,效率高。 | 只初始化一次,可以在函数调用之间保持状态。 | 必须手动释放内存,否则会内存泄漏。C++ 中建议使用智能指针。 |
BSS段
BSS 段的存在是为了优化可执行文件的大小。如果所有未初始化的变量都在可执行文件中存储零值,那么可执行文件会变得非常大。BSS 段只需要记录这些变量所需的总大小,而不需要存储实际的零值。在程序加载到内存时,操作系统会负责将 BSS 段对应的内存区域清零。这使得可执行文件更小,加载更快。
变量类型 | 初始化状态 | 内存区域 | 示例 |
---|---|---|---|
全局变量 | 已初始化为非零值 | 数据段 | int gVar = 10; |
未初始化或初始化为零值 | BSS 段 | int gUninitVar; int gZeroVar = 0; | |
全局静态变量 | 已初始化为非零值 | 数据段 | static int sgVar = 20; |
未初始化或初始化为零值 | BSS 段 | static int sgUninitVar; static int sgZeroVar = 0; | |
局部静态变量 | 已初始化为非零值 | 数据段 | void f() |
未初始化或初始化为零值 | BSS 段 | void f() { static int lsUninitVar; } void f() |
解释表格中的关键点:
- **零值 对于整数类型是
0
,浮点类型是0.0
,指针是nullptr
(C++) 或NULL
(C),布尔类型是false
。对于聚合类型(如结构体或类),所有成员都会递归地零初始化。 - 垃圾值 / 不确定值: 意味着变量内存中的内容是不可预测的,可能是之前使用该内存区域的任何数据。访问这些值会导致未定义行为,程序可能崩溃,也可能产生意想不到的结果。
- 静态初始化: 发生在程序启动时,在
main
函数执行之前。这包括零初始化和常量初始化(如果变量被常量表达式初始化)。 - 自动初始化 : 发生在进入变量作用域时,通常在栈上完成。
- 值初始化vs. 默认初始化:
- 值初始化(例如
int x{};
或new int();
)通常会导致零初始化。 - 默认初始化(例如
int x;
或new int;
)对于基本类型会产生垃圾值,但对于类类型会调用其默认构造函数。
- 值初始化(例如
- 数据段 / BSS 段:
- 数据段 (Data Segment): 存储已初始化的全局变量和静态变量。
- BSS 段 (Block Started by Symbol): 存储未初始化的全局变量和静态变量。在程序加载时,这些变量被初始化为零。
- 栈 (Stack): 存储局部变量、函数参数和函数返回地址。空间自动分配和释放。
- 堆 (Heap): 动态内存区域,由程序员手动管理分配和释放。