變量存儲機制

本文轉載於:https://www.zhihu.com/question/34266997?from=profile_question_card程序員

問題:
      好比 int a = 5

      我知道有一塊內存,存了這個值5,a表明了這塊內存。就好像給一個盒子起名叫a,這個盒子裏裝了5,現實中這個名字,也就是a只是存在人腦裏的,不是實話的。
另外a,b等字符在計算機裏用很小的整數儲存,也就是a和65對應。
    個人問題是 a = 5 的時候,有一塊內存存了5,也就是這塊內存是00000101,但計算機怎麼知道這塊內存叫a?這個名字a是在哪裏儲存的?
個人理解是否有一塊專門存儲變量名的內存,好比根據順序第x塊存了65,這個65又存了00000101的地址?但這樣豈不是每一個變量都是指針?編程

解答:數組

  變量名不佔空間數據結構

  變量:用來標識(identify)一塊內存區域,這塊區域的值通常是能夠更改的,這就是它「變」的由來,可是咱們能夠經過使用如const等一些修飾符號來限定這一內存區域的操做特性(characteristic),即變量的操做特性。用const修飾的使變量不能更改的就和常量同樣的變量叫作常變量。
  變量名:是一個標識符(identifier),用來指代一塊內存區域,即變量,使用變量使咱們操做內存以區域(area),以塊(block)爲單位,提升了方便性。
  你的機器代碼中,是不會出現變量名的;變量名是給咱們程序員操做內存來使用的。
  想一想在彙編年代,沒有變量名,咱們操做內存,都是用地址來直接操做的,還要控制區域大小;固然彙編語言已經有了簡單的變量。
  對於編譯器,它會蒐集咱們的變量名,好比咱們定義了一個全局的int a;那麼編譯器都爲咱們作了什麼呢?
  它會爲程序預留4個字節的空間(假設在32位平臺),並把咱們的變量名「a」保存進符號表,並用這個符號表的索引對應實際的空間。
  若是下面出現b = a;那麼它就會根據符號表找到變量的真正的物理位置,取得它的值,賦給b。
  這是寫編譯器須要作的,咱們須要創建符號表。
  可是實際在彙編層次上,操做的都是地址而已,不存在任何名稱了。

  除了變量名不是內存地址,其餘名都是地址。對麼?ide

  所謂的其餘名無非是函數名、標識符常量名、指針名、數組名、結構名、類名等等。
  好比指針名、數組名、函數名就是地址,它們分別表示指針所指向元素的地址、數組的首地址和函數的入口地址。
  變量名雖然不直接表示地址,但可用取地址符號&來得到它所表明的變量的存放地址。由於在定義變量的同時會分配給它相應的空間。
  但類和結構只有事例化時才爲它分配空間,從而不能用取地址符號&來得到類名或結構名的地址。函數

  變量名是用來標識某個內存塊的
  地址就是地址啦,如是變量名的話,用取地址運算符&就能夠獲得它標識的內存塊的地址,
  而指針變量呢,它自己也是一個變量名,只不過它標識的那塊內存存放的是一個地址值 工具

  變量是地址的別名..就像剛生的小孩,你只知道他在地球上的某個位置,而不能叫出他名字,給你取個名編碼

  定義int a;時,編譯器分配4個字節內存,並命名該4個字節的空間名字爲a(即變量名),當用到變量名a時,就是在使用那4個字節的內存空間.
  5是一個常數,在程序編譯時存放在代碼的常量區存放着它的值(就是5),當執行a=5時,程序將5這個常量拷貝到a所在的4個字節空間中,就完成了賦值操做.

  a是咱們對那個整形變量的4個字節取的"名字",
是咱們人爲給的,實際上計算機並不存儲a這個名字,只是咱們編程時給那4個字節內存取個名字好用.實際上程序在編譯時,全部的a都轉換爲了那個地址空間了.編譯成機器代碼後,沒有a這個說法了.a這個名字只存在於咱們編寫的代碼中.
  5不是被隨機分配的,而老是位於程序的數據段中,可能在不一樣的機器上在數據段中的位置可能不一致,它的地址其實不能以咱們經常使用到的內存地址來理解,由於牽扯到一個叫"計算機尋址方式"的問題,因此寫不少都解釋不清楚,你本身找本彙編語言的書來學一下吧.........翻譯

  C語言中變量只是標識對應存儲單元內的存儲內容。與地址的對應關係
  int a=3;
  a---&a一一對應啊,變量名只是一個便於記憶識別的名稱,編譯器會將他編譯成相應的內存地址的.變量都要佔據必定的內存。經過定義該變量的指針, [類型]* 指針名=你要指向的變量名那麼該指針中存儲的就是你的變量的內存地址。 &你的變量名 這樣就能夠直接獲取到你的變量地址或者定義引用 [類型]& 引用名=變量名該引用可經過變量的地址來對變量進行修改.指針

~~~~~~~~~~~~~~~~~~~~~~~~~

  變量名是給編譯器看的,編譯器根據變量是局部仍是全局分配內存地址或棧空間,所謂的變量名在內存中不存在,操做時轉換成地址數存放在寄存器中了。

  編譯器會將合法的變量名放到一個叫「符號表」的一個表中。
  每一個符號對應一個地址。當你調用此變量時,就會根據此符號表找到對應的地址,而後進行操做。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  尚未運行怎麼會佔用內存呢?!(這一點還要懷疑嗎!?)

  所謂在編譯期間分配空間指的是靜態分配空間(相對於用new動態申請空間),如全局變量或靜態變量(包括一些複雜類型的常量),它們所須要的空間大小能夠明確計算出來,而且不會再改變,所以它們能夠直接存放在可執行文件的特定的節裏(並且包含初始化的值),程序運行時也是直接將這個節加載到特定的段中,沒必要在程序運行期間用額外的代碼來產生這些變量。

  其實在運行期間再看「變量」這個概念就再也不具有編譯期間那麼多的屬性了(諸如名稱,類型,做用域,生存期等等),對應的只是一塊內存(只有首址和大小),因此在運行期間動態申請的空間,是須要額外的代碼維護,以確保不一樣變量不會混用內存。好比寫new表示有一塊內存已經被佔用了,其它變量就不能再用它了; 寫delete表示這塊內存自由了,能夠被其它變量使用了。(一般咱們都是經過變量來使用內存的,就編碼而言變量是給內存塊起了個名字,用以區分彼此)

  內存申請和釋放時機很重要,過早會丟失數據,過遲會耗費內存。特定狀況下編譯器能夠幫咱們完成這項複雜的工做(增長額外的代碼維護內存空間,實現申請和釋放)。從這個意義上講,局部自動變量也是由編譯器負責分配空間的。進一步講,內存管理用到了咱們經常掛在嘴邊的堆和棧這兩種數據結構。

  最後對於「編譯器分配空間」這種不嚴謹的說法,你能夠理解成編譯期間它爲你規劃好了這些變量的內存使用方案,這個方案寫到可執行文件裏面了(該文件中包含若干並不是出自你大腦衍生的代碼),直到程序運行時才真正拿出來執行!

 
 
解答二:

  我嘗試從由底向上來解釋題主的疑問,順便推薦王爽的《彙編語言》,看了前幾章題主應該就明白了,瞭解一點彙編,CPU工做原理和編譯知識仍是有必要的

  1.機器語言
  機器語言是機器指令的集合。電子計算機的機器指令是-列二進制數字。計算機將之轉變爲一列高低電平,以使計算機的電子器件受到驅動,進行運算。
  如應用8086CPU 完成運算s=768+12288-1280,機器碼以下:
  101100000000000000000011
  000001010000000000110000
  001011010000000000000101
  每行表明一個指令,我只是搬運工,反正看不懂,如今估計也找不到幾個不靠工具看得懂這玩意的人了~
  [關於變量名]:機器語言中沒有變量名的概念,一切操做都是直接對地址進行

  2.彙編語言
  早期的程序員們很快就發現了使用機器語言帶來的麻煩,它是如此難於辨別和記憶,給整個產業的發展帶來了障礙。因而彙編語言產生了。
  彙編語言與機器語言是每一個指令是一一對應的,最終由彙編器把寫有彙編語言的文本文件編譯成可執行程序。
  例如:將寄存器BX內的數據到AX中(寄存器是CPU內部的一組存儲器,大多數指令都須要先將內存裏的數據讀入寄存器後才能開始運算。AX,BX是其中兩個寄存器的代號)
  機器指令:1000100111011000
  彙編指令:mov ax,bx
  [關於變量名]:彙編語言中就有變量的概念。在編譯時由彙編器計算相關變量的偏移或實際地址,在編譯出的二進制機器語言中直接使用該地址操做內存。

   3.C語言
  程序員們後來發現,用匯編語言寫程序仍是麻煩,由於它和機器指令意義對應,更加接近CPU的思惟,而不是人的思惟。因而發明了不少高級語言,C就是其中的一種。
C語言編譯的過程,實際上首先經過 「編譯器」將C語言翻譯成彙編語言,再經過「彙編器」將彙編語言轉化成機器代碼,對於編譯器來說,將C轉化成彙編的時候,不是一一對應的關係,也就是說幾行C代碼,可能翻譯成幾十行彙編。而彙編語言指令和機器代碼指令,從某種意義來說,是一個東西,二者是一一對應的關係。
[關於變量名]:C語言中到處是變量,即便是個指針,它自身也是個4字節的變量才能儲存一個地址(32位程序)。編譯後,有輸出文件包括如下兩種:
  1. 可執行文件(xxxx.exe),一段二進制文件,其中代碼段(代碼段,數據段啥的也能夠百度瞭解)的機器指令CPU能夠直接識別執行。
  2. 符號文件(xxxx.pdb),記錄了變量和地址的對應信息。僅供調試使用,程序運行時不須要該文件。
  4.有了前面的介紹,我再來講說題主的問題
  題主問變量名儲存在哪兒,變量名儲存的機制是什麼?回答以下
  1. 程序運行時不須要知道某塊內存對應的變量名,它只是按地址直接操做那塊內存。
  2. C/C++編譯器在編譯時會計算出變量名對應的地址,在底層全部操做該變量的地方,都使用變量對應的地址參與運算。
  3. C/C++編譯器還把變量名和地址對應的關係存在一個文件中的,這類文件叫符號文件。可是程序運行時不須要這些文件,沒有這些文件程序同樣能夠正常工做。這些文件只是用來在調試時起做用的。看這份反彙編也就是利用機器語言和彙編語言指令一一對應的關係,把機器語言還原成彙編語言的結果。能夠看到,尋址都是經過直接用地址,或則寄存器+偏移量等操做完成的,關於變量名的信息在二進制可執行文件裏已經徹底沒有了。若是在調試時想要恢復這些信息,就須要用到符號文件了。再看下面這份反彙編上圖裏有move eax,dword ptr [ebp-3C] 這樣的指令, 而下圖裏有move eax,dword ptr [b]。也就是通過符號文件的幫助,在調試時能夠把[ebp-3C]這樣人看不懂的東西,還原成b這種人看得懂的變量名。在Windows下,符號文件的拓展名爲pdb,通常xxxx.pdb這樣的文件都是符號文件。若是要說計算機儲存了變量名,在彙編/C/C++下應該指的就是這種了。再次強調程序運行時不須要知道某塊內存對應的變量名,編譯器已經將C語言翻譯成二進制的機器語言,機器語言中沒有變量名的概念,一切操做都是直接對地址進行,變量名只是保存在另外一個與運行無關的文件裏供人調試時使用的。
  4. 固然上面是針對彙編/C/C++等編譯語言說的。若是是Python等解釋語言的話,其變量名和內存地址之類的信息解釋器(解釋器,編譯器分不清楚自行百度)裏是有相關記錄的,具體是什麼方式每研究過,Python能夠參考《Python源碼剖析》一書。
  5. 若是題主是想知道編譯時a的地址是怎麼肯定的,變量a及它的地址編譯器是儲存在一個什麼數據結構裏之類的話,能夠好好看看編譯原理,我沒研究過
相關文章
相關標籤/搜索