💢欢迎来到张胤尘的技术站
💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥
文章目录
- C/C++ | 每日一练 (3)
- 题目
- 参考答案
- 静态变量
- 静态局部变量
- 静态全局变量
- 静态变量可见性的底层实现
- 静态变量的初始化
- 静态局部变量
- 静态全局变量
- 内存模型
- 对象静态成员
C/C++ | 每日一练 (3)
题目
c
静态变量和 c++
静态变量的区别?
参考答案
在 c
和 c++
中 static
关键字都是用于定义静态变量的,但是在个别使用细节上还是存在一定的区别。
静态变量
不管是在 c
或者 c++
中静态变量可以用于局部静态变量和全局静态变量。
静态局部变量
定义在函数内部,生命周期为程序的整个运行时间,但作用域仅限于定义它的函数。
#include <stdio.h>
int func()
{
static int localVar = 1; // 局部静态变量,只在func内可见
++localVar;
printf("%d\n", localVar); // 2
}
int main(int argc, char const *argv[])
{
func();
// main 函数中无法访问 func 函数中的静态局部变量
return 0;
}
静态全局变量
定义在函数外部,生命周期为程序的整个运行时间,但作用域仅限于定义它的文件(即具有文件内作用域)。
#include <stdio.h>
// 全局静态变量
static int globalVar = 10; // 只在当前文件内可见
int main(int argc, char const *argv[])
{
printf("%d\n", globalVar); // 10
return 0;
}
静态变量可见性的底层实现
c
和 c++
中,静态变量的可见性(作用域和链接性)是通过编译器和链接器的机制来实现的。
分别将上面的两段代码进行编译成汇编代码,并查看代码生成的符号表,如下所示:
- 静态局部变量代码示例
$ objdump -t test.o
test.o: 文件格式 elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 l O .data 0000000000000004 localVar.0
0000000000000000 g F .text 0000000000000036 func
0000000000000000 *UND* 0000000000000000 printf
0000000000000036 g F .text 0000000000000024 main
从上面的符号表中可知,localVar
在 .data
初始化数据段,同时显示的符号绑定为 l(local)
本地符号,那么对于后续的链接阶段来说只能看到全局符号 g(global)
,所以上述代码中的 localVar
只能在本文件内可见。
- 静态全局变量代码示例
$ objdump -t test.o
test.o: 文件格式 elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 test.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l O .data 0000000000000004 globalVar
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 g F .text 0000000000000036 main
0000000000000000 *UND* 0000000000000000 printf
对于静态全局变量来说同理,从符号表中也可以看出 globalVar
在 .data
初始化数据段,也同样显示的符号绑定为 l(local)
本地符号,所以代码中的 globalVar
只能在本文件内可见。
静态变量的初始化
在 c
和 c++
中静态变量的初始化机制是一样的,但是对于其中的局部静态变量和全局静态变量来说确实存在差异。
静态局部变量
- 在程序启动时,静态局部变量的内存空间已经被分配。
- 静态局部变量的初始化是在第一次执行到变量声明语句时才发生。
- 初始化只发生一次,无论函数被调用多少次,静态局部变量都不会重新初始化。
静态全局变量
- 静态全局变量在程序加载时被分配内存,并在程序执行之前初始化。
- 静态全局变量的初始值要么是显式地由程序员设置,要么是默认初始化(例如整数默认初始化为
0
)。 - 当程序结束时,静态全局变量的生命周期才结束,它们所占用的内存才会被释放。
内存模型
静态变量在程序运行时存储在数据段中。数据段是程序内存布局的一部分,专门用于存储初始化的全局变量和静态变量。
其中数据段分为两个部分:
- 初始化数据段(Initialized Data Segment,
.data
):存储有初始值的全局变量和静态变量。 - 未初始化数据段(BSS Segment,
.bss
):存储未初始化的全局变量和静态变量,这些变量在程序启动时会被自动初始化为零。
对象静态成员
在 c++
中,类的静态成员属性是一种特殊的成员变量,它与类本身相关,而不是与类的某个具体对象相关。静态成员属性在类的所有对象之间共享,具有独特的内存模型和语义。这个在 c
中是不存在的。
静态成员属性属于类本身,而不是某个对象。因此,它们需要通过static
关键字声明,并且通常在类外进行定义和初始化。
class MyClass {
public:
static int count; // 静态成员变量声明
};
// 在类外定义并初始化
int MyClass::count = 0;
- 声明:在类内使用
static
关键字声明。 - 定义与初始化:在类外进行定义和初始化。
其中,静态成员变量的内存模型与全局静态变量类似,存储在数据段中。它们的生命周期从程序启动开始,到程序结束时结束。
- 存储位置:数据段(
.data
段或.bss
段)。 - 生命周期:从程序启动到程序结束。
- 初始化:未初始化的静态成员变量会被自动初始化为
0
或nullptr
。
另外对于访问形式来说,静态成员变量可以通过类名或对象名访问。代码如下所示:
#include <iostream>
class MyClass {
public:
static int count; // 静态成员变量
};
int MyClass::count = 0;
int main() {
std::cout << "Initial count: " << MyClass::count << std::endl; // 使用类名访问
MyClass obj;
obj.count = 10; // 不推荐通过对象调用的形式访问静态成员属性
std::cout << "Updated count: " << MyClass::count << std::endl; // 使用类名访问
return 0;
}
需要注意的是,在 c++11
标准中,支持类内初始化器对静态常量成员属性直接在类内初始化(但是必须是整形或者枚举类型,否则必须类外初始化),代码如下所示:
#include <iostream>
class MyClass
{
public:
static const int maxCount = 100; // 在类内初始化(仅适用于整型和枚举类型)
void showMaxCount() const
{
std::cout << "max count is: " << maxCount << std::endl;
}
};
int main()
{
std::cout << "max count: " << MyClass::maxCount << std::endl; // 通过类名直接访问
MyClass obj;
obj.showMaxCount();
return 0;
}
🌺🌺🌺撒花!
如果本文对你有帮助,就点关注或者留个👍
如果您有任何技术问题或者需要更多其他的内容,请随时向我提问。