- 高質量程序設計指南:C++/C語言
- 林銳 韓永泉編著
- 1677字
- 2019-01-09 14:08:59
4.2 基本數據類型和內存映像
數據類型用來定義變量的值的類型,每種數據類型對應特定的字節數。例如,在32位操作系統上,int類型的變量就擁有4字節的內存單元,而double類型的變量占據8字節的內存單元。
【提示4-2】: 字節是內存編址的最小單位。因為語言必須支持對一個變元(基本類型或復合類型的變量或對象)進行取地址運算(&),而且這個地址必須是有效的內存單元的地址,所以最小的對象(包括空對象)也至少會占據1字節的內存空間。請使用sizeof()確定數據類型在不同系統上的大小。
標準C語言支持的基本(內建)數據類型有int、long、float、double、char、void,以及它們和signed、unsigned、*、&等的組合(有些組合是不支持的,如void&)。
標準C++在這些類型的基礎上增加了bool類型,并同時增加了兩個內置的符號常量TRUE和FALSE(關鍵字)。
void是“空”類型(無值型),意思是這種類型的大小無法確定。顯然不存在void類型的對象,所以你也就不能聲明void類型的對象或是將sizeof()運算符用于void類型,C++/C語言不能對一個大小未知的對象直接操作。void通常用來定義函數的返回類型、參數列表(無參)或者void指針。void指針可以作為通用指針,因為它可以指向任何類型的對象。
【提示4-3】: 注意要分清void類型指針和NULL指針值之間的區別。NULL是可以賦值給任何類型指針的值0,在C語言環境中它的類型為void*,而在標準C++語言環境中由于允許從0到任何指針類型的隱式轉型,因此NULL就是整數0。即:
#ifdef __cplusplus #define NULL 0 #else #define NULL ((void*)0) #endif
一個void*類型的指針是一個合法的指針,常用在函數參數中來傳遞一個函數與其調用者之間約定好類型的對象地址,如在線程函數中。而一個值等于NULL的指針雖也是一個合法的指針,但不是一個有效的指針。
雖然bool類型的變量只存在兩種可能的值:true和false,按理說只需要一個bit就可以表示了。但是字節是內存編址的最小單位,而計算機從內存中提取一個變量的值是通過其地址進行的,所以一個bool變量也占據1字節內存,即sizeof(bool)等于1,浪費了7 bit。
標準C語言中沒有bool類型,但是某些實現通過庫提供了其映射,并且定義了相應的常量。例如:
typedef int BOOL; #define TRUE 1 #define FALSE 0
在標準C中,int為默認類型,也就是說如果你不明確指定函數的形參類型或函數的返回值類型,則它們的類型為int。標準C++不支持默認類型,但是在模板中有“默認類型參數”的概念。
【提示4-4】: 無論是C還是C++程序,都不要使用默認數據類型。一定要明確指出函數每一個形參的類型和返回值類型。
某些基于RISC(精簡指令集計算機)的CPU,如SPARC、PowerPC等,對內存中基本數據類型的變量采用高字節(BYTE)和高字(WORD)在低地址存放、低字節和低字在高地址存放的Big Endian存儲格式(即高字節、高字在前,或地址大的字節結尾),并且把最高字節的地址作為變量的首地址。在這種自然的存儲格式中,要求變量在內存中的存放位置必須自然對齊,否則CPU會報告異常。所謂自然對齊,就是基本數據類型(主要是short、int和double)的變量不能簡單地存儲于內存中的任意地址處,它們的起始地址必須能夠被它們的大小整除。例如:在32位平臺下,int和指針類型變量的地址應該能被4整除,而short變量的地址都應該是偶數,bool和char則沒有特別要求。所以,基于這種CPU架構的平臺,編譯器將按照自然對齊的要求來為每個變量生成邏輯地址,C++/C編譯器亦如此。例如,short型變量x和int型變量y的內存布局及其首地址如圖4-1所示。

圖4-1 Big Endian和自然對齊
Intel系列CPU采用Little Endian存儲格式來存放基本類型變量,即低字節和低字在低地址存放、高字節和高字在高地址存放(即低字節、低字在前,或地址小的字節結尾),并且把最低字節的地址作為變量的首地址。在這種硬件平臺上,上述兩個變量x和y在內存中的布局將如圖4-2所示。

圖4-2 Little Endian和自然對齊
在Intel系列CPU這種硬件平臺上,并不要求基本類型變量在內存中必須自然對齊,同樣也不會要求復合類型變量必須自然對齊。如果變量沒有自然對齊,可能會在一定程度上降低CPU訪問該變量的性能,但并不會影響程序的正確性。
對于任意類型的數組,我們知道下面的斷言都應該是真的:
SomeType x,y[100]; assert(sizeof(y)==sizeof(x)*100); //true
編譯器必須確保不僅第一個對象元素要自然對齊,而且以后的每一個對象元素也要對齊才行,所以數組的自然對齊要求和單個元素的對齊要求是一樣的。
關于復合數據類型的對齊問題,我們將在本書8.1.4節詳細講解。