C/C++中使用局部/全局變量初始值或默認(rèn)值問題
在C語言中的全局變量和靜態(tài)變量都是會自動初始化為0,堆和棧中的局部變量不會初始化而擁有不可預(yù)測的值。
C++保證了所有對象與對象成員都會初始化,但其中基本數(shù)據(jù)類型的初始化還得依賴于構(gòu)造函數(shù)。
下文來詳細(xì)探討C風(fēng)格的”默認(rèn)初始化”行為,以及C++中成員變量的初始化規(guī)則。
一、初始化的語法
很多人至今不知道C++中如何正確地初始化一個(gè)變量,我們首先來解決語法的問題。
C語言中在聲明時(shí)用=即可完成初始化操作。但我們偏向于使用C++風(fēng)格(本文中均指面向?qū)ο蟪绦蛟O(shè)計(jì)風(fēng)格)來初始化內(nèi)置類型:
// C 風(fēng)格 int i = 3; int arr[] = {1, 2, 3}; // C++ 風(fēng)格 int i(3); int i = int(3); int *p = new int(3); int* arr = new int[3] {1, 2, 3};
在C語言中int a;表示聲明了整型a但未初始化,而C++中的對象總是會被初始化的,無論是否寫了圓括號或者是否寫了參數(shù)列表,例如:
int basic_var; // 未初始化:應(yīng)用"默認(rèn)初始化"機(jī)制 CPerson person; // 初始化:以空的參數(shù)列表調(diào)用構(gòu)造函數(shù)
二、默認(rèn)初始化規(guī)則
定義基本數(shù)據(jù)類型變量(單個(gè)值、數(shù)組)的同時(shí)可以指定初始值,如果未指定C++會去執(zhí)行默認(rèn)初始化(default-initialization)。 那么什么是”默認(rèn)初始化”呢?
- 1.棧中的變量(函數(shù)體中的自動變量)和堆中的變量(動態(tài)內(nèi)存)會保有不確定的值;
- 2.全局變量和靜態(tài)變量(包括局部靜態(tài)變量)會初始化為零;
- 3.(lanyan注)函數(shù)體中的自動變量在有些編譯器下(我的是Clion2021.3),雖然在未初始化的情況下輸出也是0,但是編譯時(shí)會出現(xiàn)警告,所以這樣寫是不對的。
所以函數(shù)體中的變量定義是這樣的規(guī)則:
//C++中 int i; // 不確定值 int i = int(); // 0 int *p = new int; // 不確定值 int *p = new int(); // 0
三、靜態(tài)和全局變量的初始化
未初始化的和初始化為零的靜態(tài)/全局變量編譯器是同樣對待的,把它們存儲在進(jìn)程的BSS段(這是全零的一段內(nèi)存空間)中。所以它們會被”默認(rèn)初始化”為零。
來看例子:
int g_var; int *g_pointer; static int g_static; int main(){ int l_var; int *l_pointer; static int l_static; cout<<g_var<<endl<<g_pointer<<endl<<g_static<<endl; cout<<l_var<<endl<<l_pointer<<endl<<l_static<<endl; };
輸出:
0 // 全局變量
0x0 // 全局指針
0 // 全局靜態(tài)變量
32767 // 局部變量
0x7fff510cfa68 // 局部指針
0 // 局部靜態(tài)變量
動態(tài)內(nèi)存中的變量在上述代碼中沒有給出,它們和局部變量(自動變量)具有相同的”默認(rèn)初始化”行為。
四、成員變量的初始化
成員變量分為成員對象和內(nèi)置類型成員,其中成員對象總是會被初始化的。而我們要做的就是在構(gòu)造函數(shù)中初始化其中的內(nèi)置類型成員。
class A{ public: int v; }; A g_var; int main(){ A l_var; static A l_static; cout<<g_var.v<<' '<<l_var.v<<' '<<l_static.v<<endl; return 0; }
輸出:
0 2407223 0
可見內(nèi)置類型的成員變量的”默認(rèn)初始化”行為取決于所在對象的存儲類型,而存儲類型對應(yīng)的默認(rèn)初始化規(guī)則是不變的。 所以為了避免不確定的初值,通常會在構(gòu)造函數(shù)中初始化所有內(nèi)置類型的成員。
Effective C++: Item 4一文討論了如何正確地在構(gòu)造函數(shù)中初始化數(shù)據(jù)成員。
這里就不展開了,直接給出一個(gè)正確的初始化寫法:
class A{ public: int v; A(): v(0); };
五、封閉類嵌套成員的初始化
再來探討一下當(dāng)對象聚合發(fā)生時(shí)成員變量的”默認(rèn)初始化”行為,同樣還是只關(guān)注于基本數(shù)據(jù)類型的成員。
class A{ public: int v; }; class B{ public: int v; A a; }; B g_var; int main(){ B l_var; cout<<g_var.v<<' '<<g_var.a.v<<endl; cout<<l_var.v<<' '<<l_var.a.v<<endl; return 0; }
結(jié)果:
0 0
43224321 -1610612736
規(guī)則還是是一樣的,默認(rèn)初始化行為取決于它所屬對象的存儲類型。
封閉類(Enclosing)中成員對象的內(nèi)置類型成員變量的”默認(rèn)初始化”行為取決于當(dāng)前封閉類對象的存儲類型,而存儲類型對應(yīng)的默認(rèn)初始化規(guī)則仍然是不變的。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Linux中使用VS Code編譯調(diào)試C++項(xiàng)目詳解
最近因?yàn)轫?xiàng)目的需求,需要在Linux下開發(fā)C++相關(guān)項(xiàng)目,經(jīng)過一番摸索最終實(shí)現(xiàn)了,下面這篇文章就給大家簡單總結(jié)了一下如何通過VS Code進(jìn)行編譯調(diào)試的一些注意事項(xiàng)。有需要的朋友們可以參考借鑒,下面來跟著小編一起看看吧。2016-12-12C++類和對象深入探索之分文件編寫點(diǎn)和圓的關(guān)系詳解
先前把C++類和對象的封裝講完了,并且留下了一個(gè)判斷兩個(gè)立方體是否相等的案例,但是那么多知識點(diǎn),僅僅一個(gè)案例是不夠的,所以再來一個(gè)分文件編寫點(diǎn)圓關(guān)系的案例;創(chuàng)建圓類和點(diǎn)類,圓類包含點(diǎn)類,算是一個(gè)嵌套吧,順便復(fù)習(xí)一下分文件編寫的方法,開整2022-05-05Qt采用線程以隊(duì)列方式實(shí)現(xiàn)下發(fā)數(shù)據(jù)
在C++中隊(duì)列是一種常用的數(shù)據(jù)結(jié)構(gòu)之一,一種特殊的線性表,一般采用先進(jìn)先出的方式。本文主要為大家介紹了Qt如何以隊(duì)列方式實(shí)現(xiàn)下發(fā)數(shù)據(jù),感興趣的可以了解一下2022-10-10