一级日韩免费大片,亚洲一区二区三区高清,性欧美乱妇高清come,久久婷婷国产麻豆91天堂,亚洲av无码a片在线观看

C語(yǔ)言

詳解C/C++中堆和棧及靜態(tài)數據區

時(shí)間:2025-03-18 09:26:49 C語(yǔ)言 我要投稿
  • 相關(guān)推薦

詳解C/C++中堆和棧及靜態(tài)數據區

  本文是百分網(wǎng)小編搜索整理的關(guān)于詳解C/C++中堆和棧及靜態(tài)數據區,供參考學(xué)習,希望對大家有所幫助!想了解更多相關(guān)信息請持續關(guān)注我們應屆畢業(yè)生考試網(wǎng)!

  五大內存分區

  在C++中,內存分成5個(gè)區,他們分別是堆、棧、自由存儲區、全局/靜態(tài)存儲區和常量存儲區。下面分別來(lái)介紹:

  棧,就是那些由編譯器在需要的時(shí)候分配,在不需要的時(shí)候自動(dòng)清除的變量的存儲區。里面的變量通常是局部變量、函數參數等。

  堆,就是那些由new分配的內存塊,他們的釋放編譯器不去管,由我們的應用程序去控制,一般一個(gè)new就要對應一個(gè)delete。如果程序員沒(méi)有釋放掉,那么在程序結束后,操作系統會(huì )自動(dòng)回收。

  自由存儲區,就是那些由malloc等分配的內存塊,他和堆是十分相似的,不過(guò)它是用free來(lái)結束自己的生命的。

  全局/靜態(tài)存儲區,全局變量和靜態(tài)變量被分配到同一塊內存中,在以前的C語(yǔ)言中,全局變量又分為初始化的和未初始化的,在C++里面沒(méi)有這個(gè)區分了,他們共同占用同一塊內存區(未初始化的變量都被初始化成0或空串,C中也一樣)。

  常量存儲區,這是一塊比較特殊的存儲區,他們里面存放的是常量,不允許修改(當然,你要通過(guò)非正當手段也可以修改,而且方法很多)。

  明確區分堆與棧:

  在bbs上,堆與棧的區分問(wèn)題,似乎是一個(gè)永恒的話(huà)題,由此可見(jiàn),初學(xué)者對此往往是混淆不清的,所以我決定拿他第一個(gè)開(kāi)刀。

  首先,我們舉一個(gè)例子:

  void f() { int* p=new int[5]; }

  上面這條短短的一句話(huà)就包含了堆與棧,看到new,我們首先就應該想到,我們分配了一塊堆內存,那么指針p呢?他分配的是一塊棧內存,所以這句話(huà)的意思 就是:在棧內存中存放了一個(gè)指向一塊堆內存的指針p。在程序會(huì )先確定在堆中分配內存的大小,然后調用operator new分配內存,然后返回這塊內存的首地址,放入棧中,他在VC6下的匯編代碼如下:

  00401028 push 14h

  0040102A call operator new (00401060)

  0040102F add esp,4

  00401032 mov dword ptr [ebp-8],eax

  00401035 mov eax,dword ptr [ebp-8]

  00401038 mov dword ptr [ebp-4],eax

  這里,我們?yōu)榱撕?jiǎn)單并沒(méi)有釋放內存,那么該怎么去釋放呢?是delete p么?哦,錯了,應該是delete []p,這是為了告訴編譯器:我刪除的是一個(gè)數組,VC6就會(huì )根據相應的Cookie信息去進(jìn)行釋放內存的工作。

  好了,我們回到我們的主題:堆和棧究竟有什么區別?

  主要的區別由以下幾點(diǎn):

  1、管理方式不同;

  2、空間大小不同;

  3、能否產(chǎn)生碎片不同;

  4、生長(cháng)方向不同;

  5、分配方式不同;

  6、分配效率不同;

  管理方式:對于棧來(lái)講,是由編譯器自動(dòng)管理,無(wú)需我們手工控制;對于堆來(lái)說(shuō),釋放工作由程序員控制,容易產(chǎn)生memory leak。

  空間大。阂话銇(lái)講在32位系統下,堆內存可以達到4G的空間,從這個(gè)角度來(lái)看堆內存幾乎是沒(méi)有什么限制的。但是對于棧來(lái)講,一般都是有一定的空間大小的,例如,在VC6下面,默認的?臻g大小是1M(好像是,記不清楚了)。當然,我們可以修改:

  打開(kāi)工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后在Reserve中設定堆棧的最大值和commit。

  注意:reserve最小值為4Byte;commit是保留在虛擬內存的頁(yè)文件里面,它設置的較大會(huì )使棧開(kāi)辟較大的值,可能增加內存的開(kāi)銷(xiāo)和啟動(dòng)時(shí)間。

  碎片問(wèn)題:對于堆來(lái)講,頻繁的new/delete勢必會(huì )造成內存空間的不連續,從而造成大量的碎片,使程序效率降低。對于棧來(lái)講,則不會(huì )存在這個(gè) 問(wèn)題,因為棧是先進(jìn)后出的隊列,他們是如此的一一對應,以至于永遠都不可能有一個(gè)內存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內容已經(jīng)被彈出, 詳細的可以參考數據結構,這里我們就不再一一討論了。

  生長(cháng)方向:對于堆來(lái)講,生長(cháng)方向是向上的,也就是向著(zhù)內存地址增加的方向;對于棧來(lái)講,它的生長(cháng)方向是向下的,是向著(zhù)內存地址減小的方向增長(cháng)。

  分配方式:堆都是動(dòng)態(tài)分配的,沒(méi)有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動(dòng)態(tài)分配由alloca函數進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的,他的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放,無(wú)需我們手工實(shí)現。

  分配效率:棧是機器系統提供的數據結構,計算機會(huì )在底層對棧提供支持:分配專(zhuān)門(mén)的寄存器存放棧的地址,壓棧出棧都有專(zhuān)門(mén)的指令執行,這就決定了棧的 效率比較高。堆則是C/C++函數庫提供的,它的機制是很復雜的,例如為了分配一塊內存,庫函數會(huì )按照一定的算法(具體的算法可以參考數據結構/操作系 統)在堆內存中搜索可用的足夠大小的空間,如果沒(méi)有足夠大小的空間(可能是由于內存碎片太多),就有可能調用系統功能去增加程序數據段的內存空間,這樣就 有機會(huì )分到足夠大小的內存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。

  從這里我們可以看到,堆和棧相比,由于大量 new/delete的使用,容易造成大量的內存碎片;由于沒(méi)有專(zhuān)門(mén)的系統支持,效率很低;由于可能引發(fā) 用戶(hù)態(tài)和核心態(tài)的切換,內存的申請,代價(jià)變得更加昂貴。所以棧在程序中是應用最廣泛的,就算是函數的調用也利用棧去完成,函數調用過(guò)程中的參數,返回地 址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。

  雖然棧有如此眾多的好處,但是由于和堆相比不是那么靈活,有時(shí)候分配大量的內存空間,還是用堆好一些。

  無(wú)論是堆還是棧,都要防止越界現象的發(fā)生(除非你是故意使其越界),因為越界的結果要么是程序崩潰,要么是摧毀程序的堆、棧結構,產(chǎn)生以想不到的結 果,就算是在你的程序運行過(guò)程中,沒(méi)有發(fā)生上面的問(wèn)題,你還是要小心,說(shuō)不定什么時(shí)候就崩掉,那時(shí)候debug可是相當困難的:)

  對了,還有一件事,如果有人把堆棧合起來(lái)說(shuō),那它的意思是棧,可不是堆,呵呵,清楚了?

  static用來(lái)控制變量的存儲方式和可見(jiàn)性

  函數內部定義的變量,在程序執行到它的定義處時(shí),編譯器為它在棧上分配空間,函數在棧上分配的空間在此函數執行結束時(shí)會(huì )釋放掉,這樣就產(chǎn)生了一個(gè)問(wèn) 題: 如果想將函數中此變量的值保存至下一次調用時(shí),如何實(shí)現? 最容易想到的方法是定義一個(gè)全局的變量,但定義為一個(gè)全局變量有許多缺點(diǎn),最明顯的缺點(diǎn)是破壞了此變量的訪(fǎng)問(wèn)范圍(使得在此函數中定義的變量,不僅僅受此 函數控制)。

  需要一個(gè)數據對象為整個(gè)類(lèi)而非某個(gè)對象服務(wù),同時(shí)又力求不破壞類(lèi)的封裝性,即要求此成員隱藏在類(lèi)的內部,對外不可見(jiàn)。

  static的內部機制:

  靜態(tài)數據成員要在程序一開(kāi)始運行時(shí)就必須存在。因為函數在程序運行中被調用,所以靜態(tài)數據成員不能在任何函數內分配空間和初始化。

  這樣,它的空間分配有三個(gè)可能的地方,一是作為類(lèi)的外部接口的頭文件,那里有類(lèi)聲明;二是類(lèi)定義的內部實(shí)現,那里有類(lèi)的成員函數定義;三是應用程序的main()函數前的全局數據聲明和定義處。

  靜態(tài)數據成員要實(shí)際地分配空間,故不能在類(lèi)的聲明中定義(只能聲明數據成員)。類(lèi)聲明只聲明一個(gè)類(lèi)的“尺寸和規格”,并不進(jìn)行實(shí)際的內存分配,所以在類(lèi)聲明中寫(xiě)成定義是錯誤的。它也不能在頭文件中類(lèi)聲明的外部定義,因為那會(huì )造成在多個(gè)使用該類(lèi)的源文件中,對其重復定義。

  static被引入以告知編譯器,將變量存儲在程序的靜態(tài)存儲區而非棧上空間,靜態(tài)數據成員按定義出現的先后順序依次初始化,注意靜態(tài)成員嵌套時(shí),要保證所嵌套的成員已經(jīng)初始化了。消除時(shí)的順序是初始化的反順序。

  static的優(yōu)勢:

  可以節省內存,因為它是所有對象所公有的,因此,對多個(gè)對象來(lái)說(shuō),靜態(tài)數據成員只存儲一處,供所有對象共用。靜態(tài)數據成員的值對每個(gè)對象都是一樣,但它的值是可以更新的。只要對靜態(tài)數據成員的值更新一次,保證所有對象存取更新后的相同的值,這樣可以提高時(shí)間效率。

  引用靜態(tài)數據成員時(shí),采用如下格式:

  <類(lèi)名>::<靜態(tài)成員名>

  如果靜態(tài)數據成員的訪(fǎng)問(wèn)權限允許的話(huà)(即public的成員),可在程序中,按上述格式來(lái)引用靜態(tài)數據成員。

  PS:

  (1)類(lèi)的靜態(tài)成員函數是屬于整個(gè)類(lèi)而非類(lèi)的對象,所以它沒(méi)有this指針,這就導致了它僅能訪(fǎng)問(wèn)類(lèi)的靜態(tài)數據和靜態(tài)成員函數。

  (2)不能將靜態(tài)成員函數定義為虛函數。

  (3)由于靜態(tài)成員聲明于類(lèi)中,操作于其外,所以對其取地址操作,就多少有些特殊,變量地址是指向其數據類(lèi)型的指針,函數地址類(lèi)型是一個(gè)“nonmember函數指針”。

  (4)由于靜態(tài)成員函數沒(méi)有this指針,所以就差不多等同于nonmember函數,結果就產(chǎn)生了一個(gè)意想不到的好處:成為一個(gè)callback函數,使得我們得以將C++和C-based X Window系統結合,同時(shí)也成功的應用于線(xiàn)程函數身上。

  (5)static并沒(méi)有增加程序的時(shí)空開(kāi)銷(xiāo),相反她還縮短了子類(lèi)對父類(lèi)靜態(tài)成員的訪(fǎng)問(wèn)時(shí)間,節省了子類(lèi)的內存空間。

  (6)靜態(tài)數據成員在<定義或說(shuō)明>時(shí)前面加關(guān)鍵字static。

  (7)靜態(tài)數據成員是靜態(tài)存儲的,所以必須對它進(jìn)行初始化。

  (8)靜態(tài)成員初始化與一般數據成員初始化不同:

  初始化在類(lèi)體外進(jìn)行,而前面不加static,以免與一般靜態(tài)變量或對象相混淆;

  初始化時(shí)不加該成員的訪(fǎng)問(wèn)權限控制符private,public等;

  初始化時(shí)使用作用域運算符來(lái)標明它所屬類(lèi);

  所以我們得出靜態(tài)數據成員初始化的格式:

  <數據類(lèi)型><類(lèi)名>::<靜態(tài)數據成員名>=<值>

  (9) 為了防止父類(lèi)的影響,可以在子類(lèi)定義一個(gè)與父類(lèi)相同的靜態(tài)變量,以屏蔽父類(lèi)的影響。這里有一點(diǎn)需要注意:我們說(shuō)靜態(tài)成員為父類(lèi)和子類(lèi)共享,但 我們有重復定義了靜態(tài)成員,這會(huì )不會(huì )引起錯誤呢?不會(huì ),我們的編譯器采用了一種絕妙的手法:name-mangling 用以生成唯一的標志。

  C語(yǔ)言變量的存儲類(lèi)別

  內存中供用戶(hù)使用的存儲空間分為代碼區與數據區兩個(gè)部分。變量存儲在數據區,數據區又可分為靜態(tài)存儲區與動(dòng)態(tài)存儲區。

  靜態(tài)存儲是指在程序運行期間給變量分配固定存儲空間的方式。如全局變量存放在靜態(tài)存儲區中,程序運行時(shí)分配空間,程序運行完釋放。

  動(dòng)態(tài)存儲是指在程序運行時(shí)根據實(shí)際需要動(dòng)態(tài)分配存儲空間的方式。如形式參數存放在動(dòng)態(tài)存儲區中,在函數調用時(shí)分配空間,調用完成釋放。

  對于靜態(tài)存儲方式的變量可在編譯時(shí)初始化,默認初值為O或空字符。對動(dòng)態(tài)存儲方式的變量如不賦初值,則它的值是一個(gè)不確定的值。

  在C語(yǔ)言中,具體的存儲類(lèi)別有自動(dòng)(auto)、寄存器(register)、靜態(tài)(static)及外部(extern)四種。靜態(tài)存儲類(lèi)別與外部存儲類(lèi)別變量存放在靜態(tài)存儲區,自動(dòng)存儲類(lèi)別變量存放在動(dòng)態(tài)存儲區,寄存器存儲類(lèi)別直接送寄存器。

  變量存儲類(lèi)別定義方法:

  存儲類(lèi)別類(lèi)型變量表;

  例如:

  (1)a,b,c為整型自動(dòng)存儲類(lèi)別變量:

  auto int a,b,c;

  (2)x,y,z為雙精度型靜態(tài)存儲類(lèi)別變量:

  static double x,y,z;

  1、變量有哪些存儲類(lèi)型?

  變量的存儲類(lèi)型由“存儲類(lèi)型指明符”來(lái)說(shuō)明。存儲類(lèi)型指明符可以是下列類(lèi)鍵字之一:

  auto

  register

  extern

  static

  下面是詳細的解釋?zhuān)?/strong>

  auto 存儲類(lèi)指明符--用于說(shuō)明具有局部作用域的變量,它表示變量具有局部(自動(dòng))生成期,但由于它是所有局部作用域變量說(shuō)明的缺省存儲類(lèi)指明符,所以使用得很 少。要注意的是,所有在函數內部定義的變量都是局部變量,函數內部定義的變量其作用域只在函數內部。它的生存期為該函數運行期間,一旦離開(kāi)這個(gè)函數或這個(gè) 函數終止,局部變量也隨之消失。

  register 存儲類(lèi)指明符--當聲明了這個(gè)指明符后,編譯程序將盡可能地為該變量分配CPU內部的寄存器作為變量的存儲單元,以加快運行速度。注意,寄存器與存儲器是 不同的。寄存器一般在CPU內部,而存儲器一般指外部的(比如內存條),CPU內部的寄存器其運算速度是很高的。當寄存器已分配完畢,就自動(dòng)地分配一個(gè)外 部的內存。它的作用等價(jià)于auto,也只能用于局部變量和函數的參量說(shuō)明。

  static 存儲類(lèi)指明符--表示變量具有靜態(tài)生成期。static變量的的特點(diǎn)是它離開(kāi)了其作用域后,其值不會(huì )消失。

  當回到該作用域之后又可以繼續使用這個(gè)static變量的值。

  例:利用static變量統計調用函數的次數

  int two(); /*函數原型說(shuō)明*/

  void main()

  {

  int a=0;

  a=two(); /*a的值等于1*/

  a=two() /*a的值等于2*/

  a=two(); /*a的值等于3*/

  }

  int two()

  {

  static int b=0;    /*定義了一個(gè)局部的static變量*/

  b ;

  return b;

  }

  如果不是一個(gè)static變量就不會(huì )有這個(gè)效果了

  int two(); /*函數原型說(shuō)明*/

  void main()

  {

  int a=0;

  a=two(); /*a的值等于1*/

  a=two() /*a的值等于1*/

  a=two(); /*a的值等于1*/

  }

  int two()

  {

  int b=0;

  b ;

  return b;

  }

  變量a的值總是1,原因是在函數two()中,變量b不是一個(gè)static變量,其值隨著(zhù)離開(kāi)two函數就消失了,當回到two函數時(shí)又被重新賦值0。

  extern 存儲類(lèi)指明符--一般用在工程文件中。在一個(gè)工程文件中因為有多個(gè)程序文件,當某一個(gè)變量在一個(gè)程序文件中定義了之后,如果在另一個(gè)程序文件中予以定義, 就會(huì )出現重復定義變量的錯誤。使用extern存儲類(lèi)型指明符就可以指出在該文件外部已經(jīng)定義了這個(gè)變量。extern變量的作用域是整個(gè)程序。

  2、變量存儲在內存的什么地方?

  1)變量可以存儲在內存的不同地方,這依賴(lài)于它們的生成期。在函數上部定義的變量(全局變量或static外部變量)和在函數內部定義的static變 量,其生存期就是程序運行的全過(guò)程。這些變量被存儲在數據段(Data Segment)中。數據段是在內存中為這些變量留出的一段大小固定的空間,它分 為二部分,一部分用來(lái)初始化變量,另一部分用來(lái)存放未初始化的變量。

  2)在函數內部定義的auto變量(沒(méi)有用關(guān)鍵字static定義的變量)的生成期從程序開(kāi)始執行其所在的程序塊代碼時(shí)開(kāi)始,到程序離開(kāi)該程序塊時(shí)為止。 作為函數參數的變量只在調用該函數期間存在。這些變量被存儲在棧(stack)中。棧是內存中的一段空間,開(kāi)始很小,以后逐漸自動(dòng)變大,直到達到某個(gè)預定 義的界限。

  3)當用malloc等函數給指針?lè )峙湟粋(gè)地址空間的時(shí)候,這個(gè)分配的內存塊位于一段名為“堆(heap)”的內存空間中。堆開(kāi)始時(shí)很小,但調用 malloc或clloc等內存分配函數時(shí)它就會(huì )增大。堆可以和數據段或棧共用一個(gè)內存段,也可以有它自己的內存段,這完全取決于編譯選項和操作系統。與 棧相似,堆也有一個(gè)增長(cháng)界限,并且決定這個(gè)界限的規則與棧相同。

  C語(yǔ)言變量的作用域和存儲類(lèi)型

  一、作用域和生存期

  C程序的標識符作用域有三種:局部、全局、文件。標識符的作用域決定了程序中的哪些語(yǔ)句可以使用它,換句話(huà)說(shuō),就是標識符在程序其他部分的可見(jiàn)性。通常,標識符的作用域都是通過(guò)它在程序中的位置隱式說(shuō)明的。

  1.局部作用域

  前面各個(gè)例子中的變量都是局部作用域,他們都是聲明在函數內部,無(wú)法被其他函數的代碼所訪(fǎng)問(wèn)。函數的形式參數的作用域也是局部的,它們的作用范圍僅限于函數內部所用的語(yǔ)句塊。

  void add(int);

  main()

  {

  int num=5;

  add(num);

  printf("%d\n",num);  /*輸出5*/

  }

  void add(int num)

  {

  num++;

  printf("%d\n",num);  /*輸出6*/

  }

  上面例子里的兩個(gè)num變量都是局部變量,只在本身函數里可見(jiàn)。前面我們說(shuō)了,在兩個(gè)函數出現同名的變量不會(huì )互相干擾,就是這個(gè)道理。所以上面的兩個(gè)輸出,在主函數里仍然是5,在add()函數里輸出是6。

  2.全局作用域

  對于具有全局作用域的變量,我們可以在程序的任何位置訪(fǎng)問(wèn)它們。當一個(gè)變量是在所有函數的外部聲明,也就是在程序的開(kāi)頭聲明,那么這個(gè)變量就是全局變量。

  void add(int);

  int num;

  main()

  {

  int n=5;

  add(n);

  printf("%d\n",num);  /*輸出6*/

  }

  void add(num)   /*形式參數沒(méi)有指定類(lèi)型*/

  {

  num++;

  printf("%d\n",num);  /*輸出6*/

  }

  上面的main()和add()里面,并沒(méi)有聲明num,但是在最后輸出的時(shí)候卻要求輸出num,這是由于在程序的開(kāi)始聲明了num是全局變量,也就是在 所有函數里都可以使用這個(gè)變量。這時(shí)候一個(gè)函數里改變了變量的值,其他函數里的值也會(huì )出現影響。上面的例子輸出都是6,因為在add()函數里改變了 num的值,由于num是全局變量,就好象它們兩個(gè)函數共用一個(gè)變量,所以在main()函數里的num也隨之改變了。

  3.文件作用域

  在很多C語(yǔ)言書(shū)上,都沒(méi)有說(shuō)明文件作用域,或者只是略微的提到,其實(shí)文件作用域在較大程序中很有作用(在多文件系統中)。文件作用域是指外部標識符僅在聲 明它的同一個(gè)轉換單元內的函數匯總可見(jiàn)。所謂轉換單元是指定義這些變量和函數的源代碼文件(包括任何通過(guò)#i nclude指令包含的源代碼文件)。static存儲類(lèi)型修飾符指定了變量具有文件作用域。

  static int num;

  static void add(int);

  main()

  {

  scanf("%d",&num);

  add(num)

  printf("%d\n",num);

  }

  void add(num)

  {

  num++;

  }

  上面的程序中變量num和函數add()在聲明是采用了static存儲類(lèi)型修飾符,這使得它們具有文件作用域,僅愛(ài)定義它們的文件內可見(jiàn)。

  由于我們提到的大多數程序都只有一個(gè)編譯文件組成,所以這種寫(xiě)法沒(méi)有實(shí)際意義。但是實(shí)際工程上的文件有很多,它們不是由一個(gè)人寫(xiě)成的,由很多人共同完成, 這些文件都是各自編譯的,這難免使得某些人使用了一樣的全局變量名,那么為了以后程序中各自的變量和函數不互相干擾,就可以使用static修飾符,這樣 在連接到同一個(gè)程序的其他代碼文件而言就是不可見(jiàn)的。

  二、變量存儲類(lèi)型

  前面我們說(shuō)了,聲明變量時(shí)用如下類(lèi)似的形式:

  int num;

  float total;

  它們都沒(méi)有存儲類(lèi)型修飾符,我們在聲明時(shí)也可以通過(guò)存儲類(lèi)型修飾符來(lái)告訴編譯器將要處理什么類(lèi)型的變量。存儲類(lèi)型有以下四種:自動(dòng)(auto)、靜態(tài)(static)、外部(extern)、寄存器(regiser)。

  1.自動(dòng)存儲類(lèi)型

  自動(dòng)存儲類(lèi)型修飾符指定了一個(gè)局部變量為自動(dòng)的,這意味著(zhù),每次執行到定義該變量的語(yǔ)句塊時(shí),都將會(huì )為該變量在內存中產(chǎn)生一個(gè)新的拷貝,并對其進(jìn)行初始化。實(shí)際上,如果不特別指明,局部變量的存儲類(lèi)型就默認為自動(dòng)的,因此,加不加auto都可以。

  main()

  {

  auto int num=5;

  printf("%d\n",num);

  }

  在這個(gè)例子中,不論變量num的聲明是否包含關(guān)鍵字auto,代碼的執行效果都是一樣的。函數的形式參數存儲類(lèi)型默認也是自動(dòng)的。

  2.靜態(tài)存儲變量

  前面已經(jīng)使用了static關(guān)鍵字,但是對于局部變量,靜態(tài)存儲類(lèi)型的意義是不一樣的,這時(shí),它是和自動(dòng)存儲類(lèi)型相對而言的。靜態(tài)局部變量的作用域仍然近 局限于聲明它的語(yǔ)句塊中,但是在語(yǔ)句塊執行期間,變量將始終保持它的值。而且,初始化值只在語(yǔ)句塊第一次執行是起作用。在隨后的運行過(guò)程中,變量將保持語(yǔ) 句塊上一次執行時(shí)的值。

  看下面兩個(gè)對應的程序:

  /*1.C*/        /*2.C*/

  int add();        int add();

  main()         main()

  {          {

  int result;       int result;

  result=add()       result=add();

  printf("%d ",result);     printf("%d ",result);

  result=add();       result=add();

  printf("%d ",result);     printf("%d ",result);

  result=add();       result=add();

  printf("%d",result);     printf("%d",result);

  }          }

  int add()        int add()

  {          {

  int num=50;       static int num=50;

  num++;         num++;

  return num;       return num;

  }          }

  上面兩個(gè)源文件,只有函數add()里的變量聲明有所不同,一個(gè)是自動(dòng)存儲類(lèi)型,一個(gè)是靜態(tài)存儲類(lèi)型。

  對于1.C文件,輸出結果為51 51 51;這很好理解,每次初始值都是50,然后加1上來(lái)。

  對于2.C文件,輸出結果為51 52 53;這是由于變量是靜態(tài)的,只在第一次初始化了50,以后都是使用上次的結果值。當第一次調用add()時(shí),初始化為50,然后加1,輸出為51;當第 二次調用時(shí),就不初始化了,這時(shí)num的值為上次的51,然后加1,輸出52;當第三次調用時(shí),num為52,加1就是53了。

  比較就會(huì )發(fā)現它們的不同之處了。靜態(tài)變量在下一節要說(shuō)的遞歸函數中經(jīng)常使用到。

  當第一次不指明靜態(tài)變量的初始值時(shí),默認為0。

  下面舉一個(gè)例子,把我們說(shuō)到的靜態(tài)變量理解一下。

  求1+2+……+100的值的代碼如下:

  void add();

  int result;

  main()

  {

  int i;

  result=0;

  for(i=0;i<100;i++) add();

  printf("%d\n",result);

  }

  void add()

  {

  static int num=0;

  num++;

  result+=num;

  }

  add()函數被調用了100次,num的值從1一直變到100,這樣就可以求出它們的和了。如果寫(xiě)成int num=0;那就是求1+1+……+1這100個(gè)1的值了。

  實(shí)際上類(lèi)似的這類(lèi)問(wèn)題我們可以通過(guò)遞歸函數來(lái)解決,什么是遞歸,我們下一節介紹。

  3.外部存儲類(lèi)型

  外部存儲類(lèi)型聲明了程序將要用到的、但尚未定義的外部變量。通常,外部存儲類(lèi)型都是用于聲明在另一個(gè)轉換單元中定義的變量。下面舉一個(gè)例子,這個(gè)例子包括兩個(gè)文件。

  /*1.C*/

  void a();

  main()

  {

  extern int num;

  a();

  printf("%d\n",num);

  }

  /*2.C*/

  int num;

  void a()

  {

  num=5;

  }

  這兩個(gè)程序是分別編譯的,然后連接成一個(gè)執行文件。具體如何操作,可以查看一些手冊,這兒我簡(jiǎn)單說(shuō)了一下。把上面兩個(gè)文件都編譯好后,再制作一個(gè).prj文件,里面的內容是:

  1.c

  2.c

  只有這兩行,這可在編輯狀態(tài)下寫(xiě)成,存盤(pán),取名為1.prj。

  然后選擇project選項,選擇project name,填入1.prj文件名,按F9后,即可生成1.exe文件。

  main()函數中變量num是在另一個(gè)文件中定義的。因此,當編譯器編譯1.c時(shí),無(wú)法確定該變量的地址。這時(shí),外部存儲類(lèi)型聲明告訴編譯器,把所有對 num的引用當作暫且無(wú)法確定的引用,等到所有便宜好的目標代碼連接成一個(gè)可執行程序模塊時(shí),再來(lái)處理對變量num的引用。

  外部變量的聲明既可以在引用它的函數的內部,也可以在外部。如果變量聲明在函數外部,那么同一轉換單元內的所有函數都可以使用這個(gè)外部變量。反之,如果在函數內部,那么只有這一個(gè)函數可以使用該變量。

  前面說(shuō)了文件作用域的問(wèn)題,如果在聲明全局變量時(shí),加上static修飾符,那么該變量只在當前文件內可見(jiàn),而extern又可以引用其它文件里的變量。 所以在一個(gè)大型程序中,每個(gè)程序員只是完成其中的一小塊,為了讓自己的變量不讓其他程序員使用,保持一定的獨立性,經(jīng)常在全局變量前加static。我們可以這樣來(lái)說(shuō)明一下:

  還是上面的兩個(gè)文件,現在再增加一個(gè)文件3.c,內容為:

  static int num;

  void a()

  {

  num=6;

  }

  把1.prj文件后面加上3.c 這樣,我們生成的1.exe文件,執行時(shí)輸出是5,而不是6。因為3.c文件的num變量增加了文件作用域,在其他文件中是無(wú)法使用它的。

  4.寄存器存儲類(lèi)型

  被聲明為寄存器存儲類(lèi)型的變量,除了程序無(wú)法得到其地址外,其余都和自動(dòng)變量一樣。至于什么是變量地址,以后說(shuō)指針時(shí)會(huì )詳細介紹。

  main()

  {

  egister int num;

  num=100;

  printf("%d",num);

  }

  使用寄存器存儲類(lèi)型的目的是讓程序員指定某個(gè)局部變量存放在計算機的某個(gè)硬件寄存器里而不是內存中,以提高程序的運行速度。不過(guò),這只是反映了程序員的主觀(guān)意愿,編譯器可以忽略寄存器存儲類(lèi)型修飾符。

  寄存器變量的地址是無(wú)法取得的,因為絕大多數計算機的硬件寄存器都不占用內存地址。而且,即使編譯器忽略寄存器類(lèi)型修飾符把變量放在可設定地址的內存中,我們也無(wú)法取地址的限制仍然存在。

  要想有效的利用寄存器存儲類(lèi)型,必須象匯編語(yǔ)言程序員那樣了解處理器的內部構造,知道可用于存放變量的寄存器的數量和種類(lèi),以及他們是如何工作的。但是, 不同計算機在這些細節上未必是一樣的,因此對于一個(gè)可移植的程序來(lái)說(shuō),寄存器存儲類(lèi)型的作用不大。特別是現在很多編譯器都能提供很好的優(yōu)化效果,遠比程序 員來(lái)選擇有效的多。不過(guò),寄存器存儲類(lèi)型還是可以為優(yōu)化器提供重要的參考。

  C的作用域還有一種,靜態(tài)塊。比如:

  /* 靜態(tài)塊作用域 */

  {

  ...;

  ...;

  }

  /* 函數作用域 */

  main()

  {

  ...;

  }

  棧

  由編譯器自動(dòng)分配釋放管理。局部變量及每次函數調用時(shí)返回地址、以及調用者的環(huán)境信息(例如某些機器寄存器)都存放在棧中。新被調用的函數在棧上為其自動(dòng)和臨時(shí)變量分配存儲空間。通過(guò)以這種方式使用棧,C函數可以遞歸調用。

  堆

  需要由程序員分配釋放管理,若程序員不釋放,程序結束時(shí)可能由OS回收。通常在堆中進(jìn)行動(dòng)態(tài)存儲分配。

  非初始化數據段

  通常將此段稱(chēng)為b s s段,這一名稱(chēng)來(lái)源于早期匯編程序的一個(gè)操作符,意思是“block started by symbol(由符號開(kāi)始的塊)”,未初始化的全局變量和靜態(tài)變量存放在這里。在程序開(kāi)始執行之前,內核將此段初始化為0。函數外的說(shuō)明:long sum[1000] ; 使此變量存放在非初始化數據段中。

  初始化的數據

  通常將此段稱(chēng)為數據段,它包含了程序中需賦初值的變量。初始化的全局變量和靜態(tài)變量存放在這里。例如,C程序中任何函數之外的說(shuō)明:int maxcount = 99; 使此變量以初值存放在初始化數據段中。

  正文段

  CPU執行的機器指令部分。通常,正文段是可共享的,所以即使是經(jīng)常環(huán)境指針環(huán)境表環(huán)境字符串執行的程序(如文本編輯程序、C編譯程序、s h e l l等)在存儲器中也只需有一個(gè)副本,另外,正文段常常是只讀的,以防止程序由于意外事故而修改其自身的指令。

  對于x86處理器上的Linux,正文段從0x08048000單元開(kāi)始,棧底在0xC0000000之下開(kāi)始(棧由高地址向低地址方向增長(cháng))。堆頂和棧底之間未用的虛擬空間很大。

  Shell的size命令可以看到一個(gè)程序的正文段(text)、數據段(data)、非初始化數據段(bss)及文件長(cháng)度.

  [foxman@17:01:49 ]$size mydesign

  text data  bss  dec  hex filename

  79210 1380  404 80994 13c62 mydesign

【詳解C/C++中堆和棧及靜態(tài)數據區】相關(guān)文章:

c語(yǔ)言stack(棧)和heap(堆)的使用詳解08-23

c語(yǔ)言指針運用中堆和棧的區別10-06

Java中的堆和棧的區別10-01

c語(yǔ)言stack(棧)和heap(堆)的使用10-16

c++ 中--declspec 的用法詳解08-13

C/C++中輸入多組數據方法10-07

關(guān)于java中堆和棧的區別10-22

C語(yǔ)言中堆和棧的區別有哪些08-12

C++類(lèi)中的繼承實(shí)例詳解07-05

一级日韩免费大片,亚洲一区二区三区高清,性欧美乱妇高清come,久久婷婷国产麻豆91天堂,亚洲av无码a片在线观看