2013-03-01 280 views
12

如果所有值都不超過一個或多個字節,並且沒有字節可以包含元數據,系統如何跟蹤每個字節代表什麼類型的數字?縱觀維基百科上的Two's Complement和Single Point,可以揭示出這些數字是如何用base-two表示的,但我仍然想知道編譯器或處理器(不知道我在這裏處理的是什麼)確定了這個字節必須是是一個有符號的整數。C如何知道期望的類型?

這類似於接收一封加密的信件,看着我的密碼書架,想知道要抓取哪一個。一些指標是必要的。

如果我想到我可能會做什麼來解決這個問題,兩個解決方案浮現在腦海。要麼我要求額外的字節並用它來存儲描述,要麼專門爲數字表示分配內存部分;一個有符號數的部分,浮動部分等。

我主要在C上使用Unix系統,但這可能是一個更普遍的問題。

+5

請參閱:http://en.wikipedia.org/wiki/Symbol_table – 2013-03-01 17:24:46

+3

C中的每個變量都必須有一個類型作爲變量聲明的一部分(可能的定義,從來不知道是哪個)。編譯器讀取類型並記住它。關於變量的類型沒有神祕感。 – DwB 2013-03-01 17:25:37

+0

編譯器將使用正確的指令(因爲無符號和有符號可以用標準中規定的規則派生)。對於符號和無符號計算有不同的說明,至少對於我所見過的體系結構。對於C,在運行時不會存儲有關類型的附加信息。 – nhahtdh 2013-03-01 17:26:19

回答

9

系統如何跟蹤一個字節代表什麼類型的數字?

「系統」沒有。在翻譯期間,編譯器知道它正在處理的對象的類型,並生成處理這些值的適當機器指令。

+0

+1簡潔明瞭,像http://stackoverflow.com/users/462113/hannesh評論。 – Aubin 2013-03-01 17:49:59

+0

然後編譯器必須維護一些在編寫彙編代碼時使用的元數據,然後丟棄。這回答了我的問題。謝謝。 – 2013-03-01 18:00:06

+3

@JackStout:非常好,是的。大多數編譯器都維護所謂的*符號表*,其中包含關於對象類型,可見性,生命週期,鏈接等的信息。在翻譯階段,它用於強制執行語義規則(例如匹配賦值中的類型,或者阻止您修改「const」限定的對象)。在代碼生成階段,它被用來爲操作選擇正確的機器指令(例如,這個長字被用於浮點計算)。 – 2013-03-01 18:09:04

1

哦,好問題。讓我們從CPU開始 - 假設一個Intel x86芯片。

事實證明,CPU確實不知道一個字節是「有符號的」還是「無符號的」。因此,當您添加兩個數字或進行任何操作時,會設置「status register」標誌。

看一下「sign flag」。當您添加兩個數字時,CPU就是這樣做的 - 增加數字並將結果存儲在寄存器中。但CPU說:「如果我們將這些數字解釋爲二進制補碼有符號整數,結果是否定的?」如果是這樣,那麼「簽名標誌」設置爲1.

因此,如果您的程序關心簽名與未簽名,在彙編中編寫,您將檢查該標誌的狀態,其餘程序將執行一個不同的基於該標誌的任務。

因此,當您在C中使用signed intunsigned int時,基本上是告訴編譯器如何(或是否)使用該符號標誌。

+2

如果我沒有記錯,使用二進制補碼的優點是你只需對數字進行常規加法。 CPU不需要關心數字是否有符號/無符號/負數,它只是添加這些位。這是較高級別的代碼將值解釋爲正值或負值。 – 2013-03-01 17:34:04

+0

@SamDufel這是完全正確的。在這種情況下,標誌提供了一種方便 - 而不是寫一個例程來檢查數字的高位,你可以根據該寄存器跳轉。 (好吧,還有其他一些技巧,一個人會用這個技巧,但你的確是正確的,爲此目的,這個標誌不是絕對必要的。) – poundifdef 2013-03-01 20:37:43

1

重要的是要記住C和C++是高級語言。編譯器的工作是獲取代碼的純文本表示,並將其構建到目標平臺期望執行的平臺特定指令中。對於大多數使用PC的人來說,這往往是x86 assembly

這就是爲什麼C和C++對於如何定義基本數據類型如此鬆散的原因。例如,大多數人說一個字節有8位。這不是由標準定義的,並且對於某些機器而言,每個字節有7位作爲其本機數據解釋是沒有意義的。該標準只識別一個字節是最小的可尋址數據單元。

所以數據的解釋取決於處理器的指令集。在許多現代語言中,還有另外一個抽象,Virtual Machine

如果您編寫自己的腳本語言,則由您來定義如何用軟件解釋數據。

+0

C是一種高級語言?真?這是一個笑話,C比宏觀彙編器高出3mm! ADA是一種高級語言。 – Aubin 2013-03-01 17:38:34

+4

從技術上講,C是高級語言。就像GLSL是一種高級着色語言,可以構建到GPU的彙編級別。彙編是C構建的目標語言,技術上機器語言或者簡單的二進制數據集是最低的。人們不再認爲C或C++是高級別的,因爲我們一直認爲腳本語言就是這樣。有一段時間人們會用二進制編碼。 – 2013-03-01 17:40:23

+3

@Aubin:C每一位都像Ada那樣「高級」;它只是不提供與Ada一樣多的*抽象*。 – 2013-03-01 17:44:13

1

執行的代碼沒有關於類型的信息。知道這些類型的唯一工具是在編譯代碼時編譯器 。C中的類型只是編譯時的一個限制,以防止在某處使用錯誤的類型 。編譯時,C編譯器會跟蹤每個變量的類型 ,因此知道哪個類型屬於哪個變量。例如,

這就是爲什麼您需要使用printf中的格式字符串的原因。 printf沒有機會知道它會在參數列表中獲得什麼類型,因爲這些信息會丟失。在像go或java這樣的語言中,你有一個具有反射功能的運行時,這使得獲得類型成爲可能。

假設您編譯的C代碼中仍然有類型信息,那麼需要使用生成的彙編語言來檢查類型 。事實證明,與彙編中的類型接近的唯一東西是由suffixes (in GAS)確定的指令的操作數的大小 。因此,您的類型信息所剩下的只是尺寸而已。

支持類型的程序集的一個示例是Java VM字節碼,其類型後綴 爲operands for primitives

0

使用ç除了編譯器,是非常清楚知道的給定值的類型沒有系統是知道了給定值的類型。

請注意,C本身不帶任何運行時類型信息系統。

看看下面的例子:

int i_var; 
double d_var; 

int main() { 

    i_var = -23; 
    d_var = 0.1; 

    return 0; 
} 

在代碼有兩種不同類型的涉及一個值將被存儲爲一個整數和一個將被存儲爲一個雙精度值。

分析代碼的編譯器非常瞭解它們的確切類型。這裏的類型信息的gcc很短的片段,而通過將-fdump-tree-all到GCC生成的代碼生成舉行的轉儲:

@1  type_decl  name: @2  type: @3  srcp: <built-in>:0  
         chan: @4  
@2  identifier_node strg: int  lngt: 3  
@3  integer_type  name: @1  size: @5  algn: 32  
         prec: 32  sign: signed min : @6  
         max : @7  
... 
@5  integer_cst  type: @11  low : 32  
@6  integer_cst  type: @3  high: -1  low : -2147483648 
@7  integer_cst  type: @3  low : 2147483647 
... 

@3805 var_decl   name: @3810 type: @3  srcp: main.c:3  
         chan: @3811 size: @5  algn: 32  
         used: 1  
... 
@3810 identifier_node strg: i_var lngt: 5  

追捕你應該清楚地看到,真的有大約存儲了大量的信息@links內存大小,對齊約束和允許存儲在節點@ 1-3和@ 5-7中的類型「int」的最小值和最大值。 (我離開了@ 4節點作爲提到的「Chan」的條目只是用來Ñ起來在所生成的樹的任何類型的定義)在main.c中第3行聲明

Reagarding可變它是已知的,它保存了一個類型爲int的值,如對節點@ 3的類型引用所看到的。

如果你不相信我,他們也會在那裏,你一定能夠在自己的實驗中追捕d_var的雙重條目和那些條目。

以看看所生成的彙編程序代碼(使用gcc通過-S開關)中列出,我們可以看看編譯器使用在代碼生成這種信息的方式:

.file "main.c" 
    .comm i_var,4,4 
    .comm d_var,8,8 
    .text 
.globl main 
    .type main, @function 
main: 
    pushl %ebp 
    movl %esp, %ebp 
    movl $-23, i_var 
    fldl .LC0 
    fstpl d_var 
    movl $0, %eax 
    popl %ebp 
    ret 
    .size main, .-main 
    .section .rodata 
    .align 8 
.LC0: 
    .long -1717986918 
    .long 1069128089 
    .ident "GCC: (Debian 4.4.5-8) 4.4.5" 
    .section .note.GNU-stack,"",@progbits 

以看看您將看到編譯器找出正確的指令「mov」來分配我們的int值,「fstp」分配我們的「double」值。

儘管如此,除了在機器級選擇的指令,沒有指示這些值的類型。查看.LC0中存儲的值,值爲0.1的類型「double」甚至在兩個連續的存儲位置中被分解,每個存儲位置很長以滿足已知的「類型」彙編器。

事實上,通過這種方式打破價值只是其他可能性的一種選擇,使用8個「type」字節的連續值可以取得同樣的效果。