C++代码review问题<三>:变量默认初始化

本文详细探讨了变量的四种存储类型——自动、静态、线程和动态,以及它们在内存中的存储位置和默认初始化值。自动和动态类型的变量未初始化时可能包含随机值,而静态和线程存储类型的变量则会被零初始化。作者强调了初始化变量的重要性,以避免程序出现未定义行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题记

变量是我们编写程序的必不可少的元素(可谓程序大厦的一砖一瓦),当我们声明定义一个变量后(未手动初始化),这个变量的初始值是什么,你可能会有疑惑(如果没疑惑,请确保你是已经完全清楚而不是不关心或者不在意,因为不符合预期的变量值,往往会将程序带到undefine behavior的境地);
为了全面理解变量的初始化,按照下面几个方面进行分析:变量的种类(都有哪些变量),变量在进程内存中的存储位置、变量的初始值、变量的规范初始化

变量的种类

因为变量的存储类型决定变量何时创建、何时销毁以及它的值保持多久;我们从变量的存储类型来介绍变量,按照存储类型可以分为如下几类:

auto - 自动存储类型(内存存储在堆栈)

这种类型的对象在代码块开始的地方创建,代码块结束的地方销毁,所有的本地对象拥有这种存储类型,那些使用static、extern、thread_local关键字声明的除外;

	void func()
	{
		// create j
		int j; // auto storage duration
	} // destory j
	
	int main()
	{
		// create i
		int i; // auto storage duration
		func();
	} // destory i

static - 静态存储类型(内存存储在静态内存)

这种类型的对象在程序开始的时候创建,在程序结束的时候销毁了;整个程序中只存在一个该对象实例;所有声明在命名空间(全局、匿名、有名)的对象拥有该类型的存储类型。当再加上extern、static时,初始化会有些区别。

	namespace {
		int i; // static storage duration
	}
	namespace yms {
		int i; // static storage duration
	}
	int i; // static storage duration
	static int k; // static storage duration
	void func()
	{
		static int i; // static storage duration
	}
	int main()
	{
	}

thread - 线程存储类型(内存存储在TLS)

这种类型的对象在线程开始(默认每个线程含有一个TLS表,初始态每个线程针对线程变量的TLS表项内容相同,当线程访问变量时,会将TLS表项更新,关联至线程变量的拷贝)的时候创建,在线程结束时销毁;每个线程拥有一个该对象实例;只有当使用thread_local声明对象时,对象才拥有该存储类型。当和static、extern一起使用,只会影响链接属性。

  thread_local int i; // thread storage duration
  int main()
  {
  i++; // create i for main thread (copy i to main thread from static memory)
  std::thread t1 = std::thread([](){
  i++; // create i for t1 thread (copy i to main thread from static memory)
  } // destroy i of thread t1
  );
  
  } // destory i for main thread

dynamic - 动态存储类型(内存存储在heap)

这种类型的对象创建和销毁依赖于动态内存分配相关的函数。

变量的存储位置

类型存储位置怎么识别
auto代码块中声明的对象,除了使用static、extern
static静态内存(data)所有声明在命名空间的对象,加上代码块中使用static、extern声明的对象
threadTLS(每个线程的内存)使用thread_local声明的对象
dynamic堆,heap使用动态内存分配函数创建的对象

变量的初始值

类型存储位置默认初始化
auto随机值
static静态内存(data)Zero initialization
threadTLS(每个线程的内存)Zero initialization
dynamic堆,heap随机值

范例汇总

void func()
{
	static int i; // static storage duration: Zero initialization,0
	// create j
	int j; // auto storage duration: 随机值
} // destory j

namespace yms {
	int i; // static storage duration: Zero initialization,0
}
int i; // static storage duration: Zero initialization,0
static int k; // static storage duration: Zero initialization,0
thread_local int ii; // thread storage duration: Zero initialization,0,发生在线程第一次使用的时候。

int main()
{
	// create i
	int i; // auto storage duration:随机值
	func();
	int* val = new int(); // val point to value is dynamic storage duration:随机值; val is auto storage
} // destory i

类/结构体的成员变量

当使用static修饰时,static存储类型,存储在静态内存中,默认使用Zero initialization。
当没有static修饰时,类似auto,如果没有显示的初始化(类的初始化列表,成员初始化),值为随机值。

class Person {
	private:
		int id_;
		static int number_;
};
int Person::number_; // static storage duration: Zero initialization,0
struct Date {
	int year;
}

int main() {
	Person p; // p.id_是随机值(这个例子大概率是0,具体取决于当时分配的内存的值), p.number_为0
	Date d; // d.year是随机值(这个例子大概率是0,具体取决雨当时分配内存的值)
}

变量的规范初始化

除了static、thread存储类型的变量可以不进行显示的初始化(Zero initialization,编译器初始化为0);
剩余其他的都需要显示编写初始化,否则会是随机值,使用的时候会导致进程未定义行为。
基本变量使用operator=、{}进行初始化
对于类的成员,使用类的初始化列表,或者成员operator=,{}, 当都存在时以类的初始化列表为准。

class Person {
	public:
	Person():id_(1){} // 类的初始化列表
	private:
		int id_{1}; // 成员的初始化
};

struct Date {
	int year{1970}; // 或者 int year = 1970;
}


int main() {
	Person p;
	Date d;
	
	int i = 0;
}

最后

一定要初始化;没有初始化引起的问题,表现为未定义行为,这种是程序最糟糕的表现(比崩溃更糟糕)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值