現在市場上存活下來的 CPU 都是以 8 位元為單元, 做 2 的冪次升級. 例如: 16 位元 (8x21), 32 位元 (8x22), 64 位元 (8x23)... 而我們利用的基本資料型態也有這個現象. 例如: char 是 1 個 byte (8 位元)翻譯社 short 是 2 個 byte (16 位元)翻譯社 float 是 4 個 byte (32 位元), double 是 8 個 byte (64 位元).
有一點要注意的是 sizeof() 是一個巨集, C 編譯器會換算成正確的數值填充在程式中, 而不是程式履行的時辰才去計算結果.
要領會這個問題翻譯社 我們需要先了解天然對界.
#define SIZEOF_ARRAY(x) (sizeof(x)/sizeof(x[0]))
前面已诠釋過變數 sizeX 的問題了, 不再多說. 然則有人也許會有疑問: 變數 strY 不就只是變數 strX 換一個寫法而已嗎?
- 結構 struct
- 陣列 array
- 固定的字串 string literal
回傳值就是該成員相對於佈局位置的偏移量. 所以加上佈局的地址 (或結構指標的值) 就是該成員的現實位置了. 不外要小心計算現實位置前要把佈局的地址 type casting 成 uint8_t * (或是近似的單位偏移量為 1 的指標) 才不會算錯.
sizeof() 其實真正好用的地方在於我們沒有明白指定的元素個數的陣列上.
例如: 以 32 位元的 CPU 來說 uint16_t (巨細是 2 bytes) 必需設置裝備擺設在地址為 2 的倍數上; uint32_t (大小是 4 bytes) 必須配置在地址為 4 的倍數上, 所以 uint32_t 的變數必須安置在地址未碼 0x0翻譯社 0x4翻譯社 0x8, 0xc 的地址上; 而不行以是 0x1, 0x2翻譯社 0x3翻譯社 0x5... 等等. 不外 8 byte 巨細的 long long 或 double 則只要配置在地址為 4 的倍數上便可.
接下來我們要探討一下指標變數和 sizeof() 的關係. 先來看一下有點小機車的例子:
根基資料型態中最令天成翻譯公司們困擾唯一會有轉變的是 int 的大小究竟是 2 bytes 還是 4 bytes 或...? 不過這個問題只會在移植舊程式時才會呈現. 舊的 CPU 用的 C 編譯器和現下要用的 CPU 的 C 編譯器預設值不同, 才會呈現這樣的困擾. 有經驗的工程師 (特別是有過系統移植經驗的 embedded 工程師), 目下當今應當都知道要引入標頭檔<stdint.h>, 並改用 int16_t翻譯社 uint16_t 或者是 int32_t, uint32_t 這類明白大小的資料型態, 來避免這一類問題再呈現.
這是因為 sizeof() 計算的是寄存變數所需要的空間, 而 C 說話的字串後面必須補一個 null 字元來表示字串竣事. 是以存放字串的空間會比字串的字數再多加 1.
struct {
uint8_t ch;
uint16_t sz;
} st1;
struct {
uint8_t ch;
uint32_t sz;
} st2;
利用 16 位元的 C 編譯器來編譯時, struct st1 和 struct st2 的成員 ch 和 sz 之間會多了 1 個 byte 的 padding. 如果改用 32 位元的 C 編譯器來編譯時, 佈局成員 st1.ch 和 st1.sz 之間會多了 1 個 byte 的 padding, 但是佈局成員 st2.ch 和 st2.sz 之間則會多了 3 個 byte 的 padding. 因為 uint32_t 在 16 位元的機器上位址只要對齊到 2 的倍數上翻譯社 可是在 32 位元的機器上位址必須對齊到 4 的倍數上.
sizeof() 用在陣列變數上翻譯社 我們可以獲得全部陣列所佔用的記憶體巨細. 例如:
int arrayX[7] = { 1, 2翻譯社 3, 4, 5, 6, 7 };
int size;
size = sizeof(arrayX);
在 32 位元的 CPU 上, 天成翻譯公司們應該是獲得數值 28. 這裡要注意的是: 因為我們在前面指定要 7 個元素, 所以 C 編譯器為變數 arrayX 設置裝備擺設了 7 個 int 所需要的空間. 因此不論後面大括號中設定元素數值的個數有無填滿 7 個, sizeof(arrayX) 獲得的巨細城市是 28.
這個現象自然的擴及了 struct 內部的成員: 每個成員也都必需遵照自然對界的要求. 我們來看下面的例子:
不外對於基本資料型態翻譯社 sizeof() 根基上並沒有多大用途. 因為在一個系統上 (非論是大型主機, PC, 手機翻譯社 平板或是 embedded) 大部份的根基資料型態會佔用多大空間應該是程式師在該系統上撰寫 C 程式時最根基要知道的東西.
在 32 位元的 CPU 上, 天成翻譯公司們應當是獲得數值 24. 這裡要注意的是: 因為天成翻譯公司們在前面沒有指定變數 arrayY 到底要放多少個元素, 所以 C 編譯器根據後面大括號中設定元素數值的個數 (6 個) 來為變數 arrayY 設置裝備擺設 6 個 int 所需要的空間 24 byte. 這樣子, 變數 arrayY 的元素個數需要改變時, 我們就只需要補充或刪除後面大括號中的設定命值就能夠了.
- 界說構造 (struct) 時強制點竄為程式本來所需要的 padding (一般是不要 padding).
啊! 怎麼會如許? 過失吧! 數字字元 0~9 不是只有 10 個字元嗎? 變數 sizeX 怎麼是 11 呢?
所以 16 位元的 CPU 就要求 16 位元以上的資料型態其寄存的地址必需對齊 2 的倍數. 以此類推 32 位元的 CPU 更進一步的要求 32 位元以上資料型態的寄存地址必需對齊 4 的倍數; 64 位元的 CPU 更進一步的要求 64 位元以上資料型態的寄存地址必需對齊 8 的倍數. 這個就是所謂的天然對界.
可是翻譯社 大部份狀態下, 我們需要的是陣列的元素個數, 而不是陣列到底佔用幾何記憶體. 要獲得陣列的元素個數我們只需要把整個陣列佔用的記憶體數除以一個元素所佔用的記憶體數便可. 例如:
另外, C 說話的編譯器還有一個內建的巨集 offsetof() 可以用來獲得構造成員相對於結構地址的偏移量 (界說於標頭檔 stddef.h 中, 利用前請引入).
當然, sizeof() 也可以獲得程式中的固定字串所占用的記憶巨細, 例如:
如果我們別的界說一個字元變數 ch翻譯社 然後用 ch = strX[5]; 這一行程式取出字串中的第 6 個字元翻譯社 C 編譯器輸出的機器碼程式應當是 "由一個固定地址掏出字元". 相對 ch = strY[5]; 這一行程式輸出的機械碼程式則應當是 "由變數 strY 取出字串的地址值加 5 以後, 再以謎底看成地址來取出字元". 所以二種寫法編譯出來的程式巨細分歧, 執行速度也是分歧.
不過, 使用上面例子中的寫法來計較陣列元素個數, 有時會有點小問題: 萬一變數 arrayY 的資料型態變更了 (例如: 本來是 int, 目前因為擴充內容翻譯社 必須改成用 struct), 我們就必需要記得點竄後面的 sizeof(int) 把 int 改成准確的資料型態. 萬一忘了修改或者是打錯了, C 編譯器可不會對天成翻譯公司們提出正告. 這一點對於一些大型專案的利用其實晦氣. 所以我們需要改一下寫法:
留言列表