题记
变量是我们编写程序的必不可少的元素(可谓程序大厦的一砖一瓦),当我们声明定义一个变量后(未手动初始化),这个变量的初始值是什么,你可能会有疑惑(如果没疑惑,请确保你是已经完全清楚而不是不关心或者不在意,因为不符合预期的变量值,往往会将程序带到undefine behavior的境地); 为了全面理解变量的初始化,按照下面几个方面进行分析:变量的种类(都有哪些变量),变量在进程内存中的存储位置、变量的初始值、变量的规范初始化
变量的种类
因为变量的存储类型决定变量何时创建、何时销毁以及它的值保持多久;我们从变量的存储类型来介绍变量,按照存储类型可以分为如下几类:
auto - 自动存储类型(内存存储在堆栈)
这种类型的对象在代码块开始的地方创建,代码块结束的地方销毁,所有的本地对象拥有这种存储类型,那些使用static、extern、thread_local关键字声明的除外;
void func()
{
int j;
}
int main()
{
int i;
func();
}
static - 静态存储类型(内存存储在静态内存)
这种类型的对象在程序开始的时候创建,在程序结束的时候销毁了;整个程序中只存在一个该对象实例;所有声明在命名空间的对象拥有该类型的存储类型。当再加上extern、static时,初始化会有些区别。
namespace yms {
int i;
}
int i;
static int k;
void func()
{
static int i;
}
int main()
{
}
thread - 线程存储类型(内存存储在TLS)
这种类型的对象在线程开始(默认每个线程含有一个TLS表,初始态每个线程针对线程变量的TLS表项内容相同,当线程访问变量时,会讲TLS表项更新,关联至线程变量的拷贝)的时候创建,在线程结束时销毁;每个线程拥有一个该对象实例;只有当使用thread_local声明对象时,对象才拥有该存储类型。当和static、extern一起使用,只会影响链接属性。
thread_local int i;
int main()
{
i++;
std::thread t1 = std::thread([](){
i++;
}
);
}
dynamic - 动态存储类型(内存存储在heap)
这种类型的对象创建和销毁依赖于动态内存分配相关的函数。
变量的存储位置
类型 | 存储位置 | 怎么识别 |
---|
auto | 栈 | 代码块中声明的对象,除了使用static、extern | static | 静态内存(data) | 所有声明在命名空间的对象,加上代码块中使用static、extern声明的对象 | thread | TLS(每个线程的内存) | 使用thread_local声明的对象 | dynamic | 堆,heap | 使用动态内存分配函数创建的对象 |
变量的初始值
类型 | 存储位置 | 默认初始化 |
---|
auto | 栈 | 随机值 | static | 静态内存(data) | Zero initialization | thread | TLS(每个线程的内存) | Zero initialization | dynamic | 堆,heap | 随机值 |
范例汇总
void func()
{
static int i;
int j;
}
namespace yms {
int i;
}
int i;
static int k;
thread_local int ii;
int main()
{
int i;
func();
int* val = new int();
}
类/结构体的成员变量
当使用static修饰时,static存储类型,存储在静态内存中,默认使用Zero initialization。 当没有static修饰时,类似auto,如果没有显示的初始化(类的初始化列表,成员初始化),值为随机值。
class Person {
private:
int id_;
};
struct Date {
int year;
}
int main() {
Person p;
Date d;
}
变量的规范初始化
除了static、thread存储类型的变量可以不进行显示的初始化(Zero initialization,编译器初始化为0); 剩余其他的都需要显示编写初始化,否则会是随机值,使用的时候会导致进程未定义行为。 基本变量使用operator=、{}进行初始化 对于类的成员,使用类的初始化列表,或者成员operator=,{}
class Person {
public:
Person():id_(1){}
private:
int id_{1};
};
struct Date {
int year{1970};
}
int main() {
Person p;
Date d;
int i = 0;
}
最后
一定要初始化;没有初始化引起的问题,表现为未定义行为,这种是程序最糟糕的表现(比崩溃更糟糕)。
|