栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

题记

变量是我们编写程序的必不可少的元素(可谓程序大厦的一砖一瓦),当我们声明定义一个变量后(未手动初始化),这个变量的初始值是什么,你可能会有疑惑(如果没疑惑,请确保你是已经完全清楚而不是不关心或者不在意,因为不符合预期的变量值,往往会将程序带到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;
}
最后

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/330164.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号