初始化、顯式初始化、隱式初始化。這幾個區別是什麼?
1.如果變數不是自動變數,則只能初始化一次。
-----------------------------------------------------------為什麼只能初始化一次,那到底怎樣算初始化?自動變數就可以初始化多次了嗎?(如果多次算變數名的多次綁定嗎)初始化是變數拿到內存空間的過程嗎?2.顯示初始化是在=前用 初始化表達式 顯示的初始化變數。-------------------------------------------------------------那不是所有的賦值表達式都是變數的一次初始化過程了嗎,還是說顯示初始化與初始化不同,只有在聲明變數後的第一次初始化叫「初始化」?其他的叫「顯示初始化」。好吧,我自己也暈了。。。。。。。。。。。。。3.除自動變數外的外部變數和靜態變數,如果沒有顯示初始化的話就會隱式初始化為0-------------------------------------------------------------那初始化到底是什麼? 7月6日增加:.bbs是存放未初始化(隱式初始化)外部變數和靜態變數,程序一開始該段全部清零.data是存放顯式初始化的外部變數和靜態變數.text和.data是可執行文件中載入的,.bbs是系統初始化分配的是這樣理解嗎,但是這樣我有有問題了1 .bss和.data是靜態存儲區吧2 .text是寫入常量的吧3 .是不是可以這樣認為,.data是載入到內存後可以修改,.bss是系統初始化後可以修改, .text載入的常量不可修改
4.常量是不用分配內存的吧,在代碼區。而.data和.bbs是程序運行時要系統分配空間,.data的載入入內存分配空間,.bss是開闢一塊空間全置零。
5.字元串常量在.rodata常量去。6.const限定的外部變數在.text7.const限定的靜態變數在.bss或者.data。只是限定禁止修改
以我理解,分配空間給變數後,賦予初值稱為初始化。
非自動變數是在編譯期分配其空間,可以顯式(即手工)給予初值,否則會隱式把該空間內所有內容設為零。
自動變數是運行時每次進入函數時,才分配空間的。同一變數名實際上會對應不同的空間(例如考慮遞歸時的情況)。與非自動變數不同,自動變數是不會自隱式清零的。若沒有在定義變數時顯式初始化,未賦值前,該變數的內容是不確定值。
為什麼要有這種區別呢?都自動清零不是更安全么?
其中一個原因是,非自動變數只有一份,可以在編譯、鏈接及載入過程初始化其值,而沒有什麼運行時開銷。相反,自動變數進行初始化是有運行時開銷的。有時候我們的確只需為變數分配空間而不需初始化,例如sprintf(buffer, ...)中的buffer若是自動變數,為它初始化是徒勞無功的。所以C語言設計時為了性能,便容許自動變數不進行初始化。這和許多語言不一樣。
因為我學C語言時(25年前)只看過一些台灣中文原著,未讀過經典,以上僅是個人經驗和猜測,並未引用相關標準及文獻。簡單整理一下。C(ISO/IEC 9899:201x)5.1.2
1 All objects with static storage duration shall be initialized (set to their
initial values) before program startup.
這大概是C標準中對初始化一詞的解釋。
6.2.4
3 An object whose identifier is declared without the storage-class specifier _Thread_local, and either with external or internal linkage or with the storage-class specifier static, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
事實上由於這些對象的生存期貫穿整個程序,初始化就只能進行一次,原因望文生義就好。(不過似乎也沒哪種對象能初始化多次)
6.7.9 初始化的相關內容8 An initializer specifies the initial value stored in an object.
又強調一遍。
10 If an object that has automatic storage duration is not initialized explicitly,
似乎沒有指明什麼叫explicitly initialize,姑且理解為「定義處沒有帶有初始化器」吧。
10 If an object that has static or thread storage duration is not initialized
— if it has pointer type, it is initialized to a null pointer;— if it has arithmetic type, it is initialized to (positive or unsigned) zero;— if it is an aggregate, every member is initialized (recursively) according to these rules,
explicitly, then:
and any padding is initialized to zero bits;— if it is a union, the first named member is initialized (recursively) according to these
rules, and any padding is initialized to zero bits;
這也許就是所謂「隱式初始化」乾的活。。至於「初始化為0」就是無稽之談了。誰也沒保證空指針二進位布局全為0。(然而標準的另一個地方似乎有些微妙地否定了我這種說法)
此外,賦值和初始化沒有一毛錢的關係。單純是長得像。
下面是C++(N4296)。相比來講C++的初始化要更雜亂也更精細一些,但是它居然分在若干個章節里。。3.6.22 Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5)
before any other initialization takes place.
這說明一次完整的初始化過程可能會發生多次初始化。。
同時這一節定義了若干術語,比如constant initialization,static initialization,dynamic initialization。3.81 The lifetime of an object of type T begins when:
— storage with the proper alignment and size for type T is obtained, and
— if the object has non-vacuous initialization, its initialization is complete.
這指明了(non-vacuous)初始化的意義:它開啟了對象的生存期。此外,也說明了初始化並不是確定對象所佔的存儲。
6.72 Variables with automatic storage duration (3.7.3) are initialized each time their declaration-statement is
executed.
注意variable在C++中是有明確定義的術語。(C中則沒有)
8.51 A declarator can specify an initial value for the identifier being declared.
這個說法和C中的差不多。
這裡也定義了若干類型的初始化:zero-initialize default-initialize value-initialize direct-initialize。此節很長,把大多數的初始化都包括了進去。
同樣的,這裡也使用到了explicitly initialize卻沒有定義。只好同樣理解成「具有初始化器的初始化」了。就這樣。看了這個問題,感覺挺悲哀。就是這是教學嗎。。。。這些觀點描述的不清不楚,也不給人解釋時候什麼意思,就類似高大上的拋出幾個結論,如果你不知道他在說什麼,那麼你怎麼對待,只是機械的記憶這些毫無生氣的觀點?而且這些觀點描述的是否嚴謹,還是個疑問!總感覺是不嚴謹的人把自己的觀點,跑出來當真理,真可謂害人不淺。
最大的問題是沒有從本質上,原理上去解釋問題,而是膚淺到一些教條主義上面去了。。。好像總結出了什麼牛逼的規律一樣。實際上卻無卵用!
什麼叫非自動變數,這一句話就已經讓人感到眩暈了。相對於非自動變數,那就得知道變數從存儲空間的分配角度看,可以怎麼區分呢: auto (默認)/register (程序員建議為此變數優先分配到寄存器),static(靜態全局),
auto 因為是默認,所以代碼里通常不見此關鍵字。簡單說,就是函數里的臨時變數,存儲在 stack 上,生存周期短,編譯器自動維護它的生存周期。
register:隨著程序複雜度提高,後端優化器的編譯器理論的發展,程序員參與建議到優化過程,已經不再是一種提倡(因為弄不好你很可能起到一個搗亂和幫倒忙的作用)。現在基本也是廢棄狀態。可以認為和 auto 是等效。例如是否 inline ,什麼除法變乘法,位運算,等大多數都不需要程序員在裡面操心。
static:靜態變數。指生存周期在程序運行期間,都有效的那些變數。
這個繼續的細分類比較煩。因為涉及讀寫許可權,被分配到兩個 data section/segment,分別是可讀寫(全局變數,函數內的 static 變數)。只讀(主要由是大量的你的代碼中出現的那些手寫的字元串組成)。這裡,根據這些高大上的結論,我們推測特指前者:可讀寫的 static 變數(可見性為進程空間內全局可見,或者某函數內部可見)。
現在我們把結論(1)進行根據我們對說出這個結論的人的意圖的理解,做出重解釋。
對方提的」非自動變數「,我們認為,特指,可讀寫的全局變數和函數內可見的 static 變數。對結論(1),對全局變數強調這個結論顯然有些莫名其妙,所以很顯然它可能針對的函數內的 static 變數的情況:
void foo()
{static int x = 100;x++;printf("%d", x);};
我們對比:
void foo()
{int x = 100;x++;printf("%d",x);}
可以很明顯的得出結論:前者的賦值,只在程序載入時執行過一次。效果等效於首次發現時執行,當第二次,第三次,。。。第 N 次調用時,這一行代碼就好像被 bypass 了一樣(bs的書里就是這麼介紹的,請注意我使用的詞語是等效於,意味著你可以這樣理解,但具體細節是這樣的,static int x = ... 這一行代碼在編譯後根本不會出現在這個函數的函數體中)(對於這種全局變數,因為 initialized data 的值已經寫在可執行文件的對應的數據段中了,所以就是在 」load「 / 」映射到內存鏡像「 過程中完成的)。對於類的構造,是在 load 之後,在 mainCRTStartup 中,調用程序員提供的入口函數之前構造的(在vc 中,由 _intterm 的一個函數來完成所有全局對象的構造)。
後者:
很顯然,伴隨每次調用,都會執行一次。對比一下這兩者的區別,那麼我們有理由懷疑,這是強調」一次「和」多次「的來歷,至少是之一。這裡還有一個」初始化「的概念,在這裡我們就解釋成對象通常在分配空間後,對他構造,賦值,初始化的過程。如果這樣定義初始化,就和之後的」更改「」賦值「」拷貝「分開,那麼任何對象,豈不都是只能初始化一次么!那麼這結論是不是很無厘頭?
我們現在回頭來看單獨的看第一個結論,非自動變數,只能初始化一次。是不是很無厘頭啊!如果不解釋清楚,你能知道他的真實意圖和含義嗎!所以我要說這種」高大上「結論,不是一般坑跌!!!!
(眾所周知:如果只聲明了一個 static int
x,不寫後面的初始化值呢,那麼這個對象的初始值就是0。這個是和auto變數的很大區別,autu
變數如果不給他賦值,release的時候是誰也不確定的值,debug版本棧上分配的所有空間會先被成填充0xcc(INT
3),這就是燙燙燙的由來。)
好吧,寫到這,我忽然發現結論(2)和(3)的意思也得到了。。。
2.定義什麼叫做顯示初始化;3.除自動變數外的外部變數和靜態變數,如果沒有顯示初始化的話就會隱式初始化為0這句話的意思是,
int x,*p;如果這是全局變數:則 x 的初值是 0,p 的值是 NULL(因為指針 NULL 通常就是0)。函數內的 static 變數同理。如果這是函數內定義的 autu (臨時)變數呢,我已經說過了,
debug:版本,所有位元組都是 0xccrelease:在你賦值之前,你沒動他們,編譯器也沒動他們,所以這些變數初始值不確定,取決於運行時的即時狀態。當時的值,誰知道呢,也許是之前的函數在哪裡留下的痕迹,所以 p 這個時候通常就是野指針了。這個結論里摻雜了顯示,隱式初始化,個人覺得不是很標準和很通用的術語,當然了不影響理解的話也是沒什麼問題,結論(3)描述的這個東西,倒是很重要!是所有 c,c++程序員都必須要知道的。
我們根據理解,推斷出結論(3)中的術語定義,所謂」隱式「,由編譯器,loader,linker三者之間合作或者編譯器運作的對程序員」不可見「動作,行為,即這裡的」隱式「。所謂」顯式「,是程序員可以在高級語言層面覺察到的行為。學過 c++ 的人都知道,這在 c++ 中很常見,可能也是讓人感到 c++ 比較困難的原因之一吧。
寫的太長不太好,不利於別人抓住重點。。。
一時半會這個問題不容易展開敘述清楚。所以我還是先佔著這個位子。以後在展開寫吧。
不過我懷疑你的原題的描述中有錯誤:
.bbs 懷疑應該是筆誤,實際是 .bss:
.bss: Uninitialized global C variables. 這個「段/節」不會在磁碟上佔據實際存儲空間。他只是起到一個提示佔位作用(我需要這麼大的一個輔助性的空間)。區分 initialized 和 uninitialized 變數的目的,是為了空間效率。(節省磁碟空間)。根據資料,
bss 最早起源於 IBM 彙編語言中的 "block storage start" 指令。bbs:眾所周知,是電子公告板系統(Bulletin Board System)的縮寫。早期以 terminal 方式交互的論壇,現在已經沒落了,現在 bbs 的用戶很少。好想邀請折騰編譯器的藍色大大回答這個問題。。。
我也想試著答這個問題呢。。。占坑先。。。。不是譚浩強的,你買到盜版了吧。
我覺得不用咬文嚼字,高級語言是工具,知道怎麼用就行了。養成好的編程習慣,變數用之前要附初值,指針要賦空或新分配或指向某變數,基本可以避免語言本身的坑。至於這3種初始化這麼形而上的東西在實踐中,我是看不出有啥差別,就留著給程序屆的「哲學家們」研究吧。
貼個轉載的,應該便於新手理解
-----------------------------------------------------------------
分配空間給變數(即賦值),賦予初值即為初始化。初始化值必須是編譯時可確定的。如果沒有初始化語句,欄位的值會被編譯器設為默認值,默認值由欄位的類型決定。
比如:下面這兩種初始化即為隱式初始化
int F1; //初始化為0
string F2; //初始化為null下面這兩種初始化為顯式初始化
int F3 = 25; //初始化為25
string F4 = "abcd"; //初始化為「abcd」顯式初始化即為手工給予初值,否則為隱式初始化,將內容設置為默認值。
自動變數在運行時進入函數的時候,才進行分配空間賦值。非自動變數會自隱式清零,而自動變數是不會自隱式清零的。若沒有在定義變數時顯式初始化,未賦值前,該變數的內容是不確定值。
初始化:聲明時給的值。靜態變數的隱示初始化,是在載入的時候,.bss區直接初始化為0。即不賦值,靜態變數為0。
根據題主上傳的圖片,猜測題主只想了解c語言而不是c++,所以以下略去c++的內容。首先要區分初始化和賦值。初始化僅僅在定義變數時發生!就是你第一次寫下這個變數的時候。其他時候都是賦值操作。弄清楚這個,問題就解決了
@Milo Yip大神已經詳細耐心地解釋得很清楚了,另外你拍的那張圖片上的書我宿舍也有一本,我剛剛翻了一下,確實翻譯得不適合你看,如果你真想看就看英文版吧。
個人理解,當變數聲明通過語法句法解析之後轉為openCode經過編譯到達代碼段之後,在棧區與數據段的變數區建立關係之後才是真正的初始化;而自動變數可能是在編譯過程(也可能是在解析語法句法)中產生指向所在自動變數(我記得有種寫時複製機制,當varA=0,varB=a時,兩者在棧創建一個不同的標誌,但是兩者都指向同個數據段變數區,直到對varB=1時才會在在數據段變數區開闢指向,如果按照實際上面來推論應該在varB=1時開闢指向才是初始化)
當然,以上是我主觀臆斷初始化:老王對老婆說我想有個娃(string baby="";);
顯式初始化:老王給娃取名為李狗蛋男一號(string baby=DogEggMaleNo1);即娃出生之後再取名字。
隱式初始化:一年後,老婆又懷上了,老王想下一個如果是女娃就叫李狗蛋女一號,如果是男娃就叫李狗蛋男二號(string baby2=new Baby(boy=DogEggMale2, girl=DogEggFemaleNo1)),即名字已經取好,就等娃出生---------------------------------------int a; //這是定義,也是隱式的初始化,a被默認初始化為0int b=10; //這是顯示初始化,明確的制定b的值int a=new IntClass(val=10); //這是隱式初始化,在類的內部預先設定一個值作為默認值---------------------------------------個人觀點,請大神們批評,匿了。推薦閱讀:
※在使用lib時,代碼都被鏈接到exe中去了嗎?
※學習C++,應該循序漸進的看哪些書?
※和團隊做物理引擎,做到商業程度還需要什麼?
※編程時IDE里的Intellisence好像是個編譯器前端一樣,什麼都知道.這是怎麼實現的?