Re: [問題] auto宣告的記憶體問題

看板C_and_CPP (C/C++)作者 (藍影)時間14年前 (2012/01/19 18:54), 編輯推噓8(8015)
留言23則, 12人參與, 最新討論串4/4 (看更多)
我先說結論, 你在 auto 那裡看到的「動態」、「靜態」的修飾全都拿掉, 一方面他的語意讓大多學子誤會 (我相信筆者本意也不是如此), 另一方面照他邏輯走也不完全正確。 由於這本書我沒全看完 (其中一個原因是我覺得有時敘述會讓人有點誤解) 我們暫時拋棄你手邊書本的語意,換個說法來過。 同時我也只針對你書本之文意做部份說明,太深入的我就不再多敘述, 所以 register、static、global variable、constant 並不在此文探討範圍內。 ---- ※ 引述《amozartea (單車單車)》之銘言: : 如題,這可能很少人問過 : 書上寫說區域變數其實都自動預設auto, : 在編譯過程中並不會配置一塊記憶體空間,而是在程式執行時以堆疊的方式存放, : 因此他是屬於動態的變數。 : ^^^^^^^^^^^^^^^^^^^^^^ 此段是出自洪維恩的書 這段省略了很多東西,洽巧初學者可能也不要知道會比較好, 因為可能會打擊學習程式的信心。 下面五行很重要,請記起來,沒記起來的話這篇文章你看不懂。 一個最重要、基本的觀念是:不論程式裡面的變數宣告成怎樣, 最終都會被放在記憶體裡面,而程式在執行時,這些記憶 體又被畫分為若干區段,其中你書本提到的是 stack (堆疊段) 與 heap (這個怎麼翻?) , 這兩個名詞以後請用英文寫出來, 我相信會比中文表達深切很多。 原文這段有四個字是重點: 編譯過程 ,指的是 編譯期 也就是上面這些動作是你在使用 dev-c++ / vc / gcc 等編譯器時, 就決定動作這些變數是要放 堆疊 段。 ----- 引言 1 在編譯過程中並不會配置一塊記憶體空間, 而是在程式執行時以堆疊的方式存放,因此他是屬於動態的變數。 若照 C language 之定義,local variable 加 auto 時,其實有寫跟沒寫是一樣的。 也就是說 { int i; // auto int i; 同義 int arr[3]={1,2,3}; // auto int arr[3]={1,2,3}; 同義 int *ptr=NULL; // auto int* ptr=NULL; 同義 } 上述三個變數,在編譯時,是由編譯器規劃其空間,最後是放在 stack 裡面, 而這類型的變數有個特性:在程式執行的時候(稱執行期),這些變數可能會改變。 這是我猜測書本對於「動態」之解釋。 補註,這麼解釋真的不盡人意,因完全忽略了 static variable - static int a=10; 這才是真正的 static variable, 還有一類型的變數,他們在執行的時候並不會改變他們的內含值 (value), 如 const variable ,像是這段 code { const char* txt="hello, world!!"; const double PI=3.1415926; } 在程式執行txt 和 PI 從頭到尾都不可能會被改變(又是一個錯誤的思考:靜態), 它們不是被放在 stack 裡面,遺憾的也不是放在 heap 裡, 而是被放在其它的記憶體區段裡,這區段就叫 .rodata 區段,都是放唯讀的資料。 ( 這也是那本書沒提到的部份 ) 上面這段的重點只是:變數不宣告成 const 的話,是放在 stack , (雖這句話一點點問題..),完畢。 ----- : 但是又在動態記憶體章節上面寫說C++都是預設靜態什麼的 很讓人搞混 我猜書本上指的動態記憶體,大多指的是動態陣列 (雖然有時不一定要是陣列..) 截至目前為止,上敘其實只有提到 auto variable 是放在 stack 裡面。 接下來所有講「動態記憶體」,指的全部都是用 new / malloc 指令的東西, 但我很怕你會愈來愈亂,所以這部份重頭簡述至尾。 後半段必須探討到指標的東西,不熟的話再去複習一次。 一般而言,如果是寫 int arr[5] = {1,2,3,4,5}; 這種方式宣告陣列的話,因為大小一開始就固定,在程式的 編譯期 就可以算出要多大, 且在 執行期 不可以改變陣列的大小,屬於靜態陣列。 ( 還記得上一段有提到嗎?靜態陣列是放在 stack 裡面。) 但有時候,陣列要給多大必須在執行期的時候才可以知道, 比如說,先輸入班上有幾個人,再輸入每個人的成績,再做平均。 這時候沒辦法在 編譯期 時候就知道人數,此時會使用動態配置。 (如果扣掉被大多數人所垢病的 可變長度陣列,VLA, 作法時,只剩動態配置一途) 另,希望你沒忘記一件事:即使是指標,本身存的是「記憶體位址」, 故你常看到的動態配置的東西 { int *a = NULL; a = new int[10]; for(int i=0; i<10; ++i) a[i] = i; } 注意的是, a 本身是指標,它是在 編譯期 裡面就決定的東西, 是放在 stack 裡面。 而 a = new int[10]new int[10] 一開始先在 heap 上配置 10 個 int 大小, 最後把這 heap 的 "第一個位址值" 再傳回來給 a, 意思是如果配置的空間是 0x90000000 0x90000004 ..... 最後 a 會存的是 0x90000000。 但這並不改變, a 本身是在 stack 上的事實, 會在 heap 上操作,只有這些動作: *a=1 、 *(a+1)=10 、 a[1] = 10 因為是籍由指標 a ,去操作剛剛在 heap 上 new (這動作叫 allocate) 出來的記憶體。 所以呢? int a[3]={1,2,3}; // 靜態記憶體陣列,放在 stack int *a = new int[3]; // a 本身是指標,放在 stack; // 而 a[i] 或 *(a+i) 本身放在 heap。 那放在 stack 和 heap 上有什麼差別? 速度 (access speed) 應是一樣的, 有幾個地方是顯然不同。 (a) 要用 heap 必須調用 new / malloc 相關指令 (在不考慮使用低階 api 情況下) (b) 靜態陣列、一般變數 (扣除 const、static、global,但含 pointer), 是放在 stack 裡面,這在 編譯期 就可以由編譯器規劃好的東西; 而 heap 在編譯時要多大不知道,是在 執行期 時才由作業系統幫忙規劃。 (c) stack 可使用的空間範圍較小,而 heap 可使用的空間範圍較大; 所以,若陣列的元素個數很多,即使元素個數是固定的,也必須在 heap 上使用, 而不能放在 stack 裡面。意指:去試試 double x[2000000]; 好一點的 compiler 可能會有警告還是錯誤,這時候必須用 int *x=new int[2000000]; : ok 總之我目前的認知是一般的陣列是靜態宣告... : (以下都是區域變數) : int a; 跟 auto int a; 等價,而且這是動態 : int a[5]; 是靜態,,,應該吧? 這裡不要記動態、靜態,書本要表達的意思是, int a; ---> 它是放在 stack 裡面 (用 動態 來形容就差了) int a[5]; ---> 它是靜態的記憶體,因為不是用 new 運算子配置出來的, 也是放在 stack 裡面。 : 那假設我宣告如下(當然是區域): : auto int a[5]; 這到底該算靜態還是動態? 已經試過編譯可以過... 完整的說法是: 因為它是 local auto variable,而不是 const variable (唯讀變數), 它的值可以被改變,放在 stack 裡面。 而陣列的大小固定為 5 ,在編譯期就可以知道的大小,是靜態陣列 也由於它不是唯讀,也是放在 stack 裡面。 : 如果是靜態那不是表示auto關鍵字在這裡沒用嗎@@? : 如果是動態的話那跟 : int* a; a = new int[5];又有何不同 這點上面有說過了,細閱。 (結語 : 我可以說為什麼我不推這本書的原因了嗎? XD) -- 世界上有種, 將 不可能 轉換為 無限可能 的強大力量, 我稱它為 - 信念 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 180.177.69.239

01/19 19:47, , 1F
auto 之所以叫 auto,是因為不用手動控制它的生死。
01/19 19:47, 1F

01/19 19:48, , 2F
中文書用的詞彙,在講解這東西時很容易造成混亂是真的。
01/19 19:48, 2F

01/19 21:12, , 3F
推用心講解XD
01/19 21:12, 3F

01/19 21:20, , 4F
我只能說t大全部講解完了,實際上local variable預設
01/19 21:20, 4F

01/19 21:22, , 5F
是auto變數.
01/19 21:22, 5F

01/19 22:45, , 6F
推解釋詳細 只能說作者這樣寫很容易讓初學者誤會
01/19 22:45, 6F

01/19 22:53, , 7F
第一次聽到用auto來說,就說是local會alloca在stack不
01/19 22:53, 7F

01/19 22:53, , 8F
就OK了..
01/19 22:53, 8F

01/19 22:57, , 9F
但 local 不一定會 allocate 在 stack 下啊 ~
01/19 22:57, 9F

01/19 22:59, , 10F
好吧,或許我不該特別挑這問題出來講 XD
01/19 22:59, 10F

01/19 23:44, , 11F
可以問您 DLL載入的東西,他們的記憶體位置又會在哪裡?
01/19 23:44, 11F

01/19 23:46, , 12F
是OS幫忙載入,所以是在OS的另外的記憶體區段中?
01/19 23:46, 12F

01/19 23:51, , 13F
謝謝,請推薦我有寫這方面部份的書...
01/19 23:51, 13F

01/19 23:52, , 14F
dll問題扯太遠了,另開主題佳. 書:程式設計師的自我修養
01/19 23:52, 14F

01/19 23:53, , 15F
我讀過的都是語法和資料結構書等等都沒有講這方面QQ
01/19 23:53, 15F

01/20 00:01, , 16F
長知識!
01/20 00:01, 16F

01/20 00:56, , 17F
我也推:程式設計師的自我修養!
01/20 00:56, 17F

01/20 01:00, , 18F
local不會allocate在stack是指static var還是被優化?
01/20 01:00, 18F

01/20 01:02, , 19F
@LifePattern : static variable. const variable.
01/20 01:02, 19F

01/20 01:19, , 20F
謝了,忘了const XD
01/20 01:19, 20F

01/20 07:07, , 21F
t大文 只有推了
01/20 07:07, 21F

01/20 17:25, , 22F
推用心+1
01/20 17:25, 22F

02/03 17:45, , 23F
有學到!推一個
02/03 17:45, 23F
文章代碼(AID): #1F5_Pv6i (C_and_CPP)
文章代碼(AID): #1F5_Pv6i (C_and_CPP)