物件與對齊
C 程式建立、銷燬、訪問和操作物件。
C 中的物件是執行環境中資料儲存的一個區域,其內容可以表示值(值是物件內容在被解釋為具有特定型別時的含義)。
每個物件都具有
- 大小(可以用
sizeof
確定) - 對齊要求(可以用
_Alignof
(直到 C23)alignof
(自 C23 起) 確定)(自 C11 起) - 儲存期(自動、靜態、分配、執行緒區域性)
- 生命週期(等於儲存期或臨時)
- 有效型別(見下文)
- 值(可能不確定)
- 可選地,一個表示此物件的識別符號。
物件透過宣告、分配函式、字串字面量、複合字面量以及返回帶有陣列成員的結構體或聯合體的非左值表示式來建立。
目錄 |
[編輯] 物件表示
除了位域之外,物件由連續的一個或多個位元組序列組成,每個位元組包含 CHAR_BIT 位,並且可以用 memcpy 複製到 unsigned char[n] 型別的物件中,其中 n
是物件的大小。結果陣列的內容被稱為物件表示。
如果兩個物件具有相同的物件表示,則它們比較相等(除了浮點 NaN)。反之不成立:兩個比較相等的物件可能具有不同的物件表示,因為並非物件表示的每個位都需要參與值。這些位可能用於填充以滿足對齊要求、進行奇偶校驗、指示陷阱表示等。
如果物件表示不表示物件型別的任何值,則稱其為陷阱表示。以除透過字元型別的左值表示式讀取之外的任何方式訪問陷阱表示都是未定義行為。結構體或聯合體的值絕不是陷阱表示,即使其任何特定成員是陷阱表示。
對於 char、signed char 和 unsigned char 型別的物件,物件表示的每個位都必須參與值表示,並且每個可能的位模式表示一個不同的值(不允許填充、陷阱位或多種表示)。
當整型物件(short、int、long、long long)佔用多個位元組時,這些位元組的使用是實現定義的,但兩種主要實現是大端(POWER、Sparc、Itanium)和小端(x86、x86_64):大端平臺將最高有效位元組儲存在整數所佔用儲存區域的最低地址,小端平臺將最低有效位元組儲存在最低地址。詳細資訊請參閱位元組序。另請參閱下面的示例。
儘管大多數實現不允許整型型別的陷阱表示、填充位或多種表示,但也有例外;例如,Itanium 上的整型值可能是陷阱表示。
[編輯] 有效型別
每個物件都有一個有效型別,它決定了哪些左值訪問是有效的,哪些違反了嚴格別名規則。
如果物件是透過宣告建立的,則該物件的宣告型別是該物件的有效型別。
如果物件是透過分配函式(包括 realloc)建立的,則它沒有宣告型別。此類物件按如下方式獲取有效型別
- 首次透過非字元型別的左值對該物件進行寫入,此時該左值的型別成為此物件用於該寫入和所有後續讀取的有效型別。
- memcpy 或 memmove 將另一個物件複製到該物件中,或將另一個物件作為字元型別陣列複製到該物件中,此時源物件的有效型別(如果它有)成為此物件用於該寫入和所有後續讀取的有效型別。
- 任何其他對沒有宣告型別的物件的訪問,有效型別是用於訪問的左值的型別。
[編輯] 嚴格別名
給定一個具有有效型別 T1 的物件,使用不同型別 T2 的左值表示式(通常是解引用指標)是未定義行為,除非
- T2 和 T1 是相容型別。
- T2 是與 T1 相容的型別的 cvr-限定版本。
- T2 是與 T1 相容的型別的有符號或無符號版本。
- T2 是聚合型別或聯合體型別,其成員中包含上述型別之一(包括遞迴地,子聚合或包含聯合體的成員)。
- T2 是字元型別(char、signed char 或 unsigned char)。
這些規則控制著在編譯一個接收兩個指標的函式時,編譯器是否必須在透過一個指標寫入後重新讀取另一個指標。
// int* and double* cannot alias void f1(int* pi, double* pd, double d) { // the read from *pi can be done only once, before the loop for (int i = 0; i < *pi; i++) *pd++ = d; }
struct S { int a, b; }; // int* and struct S* may alias because S is an aggregate type with a member of type int void f2(int* pi, struct S* ps, struct S s) { // read from *pi must take place after every write through *ps for (int i = 0; i < *pi; i++) *ps++ = s; }
請注意,即使上述規則允許它們別名,restrict 限定符也可以用於指示兩個指標不別名。
請注意,型別雙關也可以透過聯合體的非活躍成員執行。
[編輯] 對齊
每個完整的物件型別都有一個名為對齊要求的屬性,它是一個 size_t 型別的整數值,表示該型別物件可以分配的連續地址之間的位元組數。有效的對齊值是非負的2的整數冪。
(C11 起) |
為了滿足結構體所有成員的對齊要求,可能在其某些成員之後插入填充。
#include <stdalign.h> #include <stdio.h> // objects of struct S can be allocated at any address // because both S.a and S.b can be allocated at any address struct S { char a; // size: 1, alignment: 1 char b; // size: 1, alignment: 1 }; // size: 2, alignment: 1 // objects of struct X must be allocated at 4-byte boundaries // because X.n must be allocated at 4-byte boundaries // because int's alignment requirement is (usually) 4 struct X { int n; // size: 4, alignment: 4 char c; // size: 1, alignment: 1 // three bytes padding }; // size: 8, alignment: 4 int main(void) { printf("sizeof(struct S) = %zu\n", sizeof(struct S)); printf("alignof(struct S) = %zu\n", alignof(struct S)); printf("sizeof(struct X) = %zu\n", sizeof(struct X)); printf("alignof(struct X) = %zu\n", alignof(struct X)); }
可能的輸出
sizeof(struct S) = 2 alignof(struct S) = 1 sizeof(struct X) = 8 alignof(struct X) = 4
每個物件型別都對其該型別的每個物件施加其對齊要求。最弱(最小)的對齊是 char、signed char 和 unsigned char 型別的對齊,等於 1。任何型別的最嚴格(最大)基本對齊是實現定義的並等於 max_align_t 的對齊(自 C11 起)。
所有儲存期型別的物件都支援基本對齊。
如果使用 如果結構體或聯合體型別 |
(C11 起) |
[編輯] 缺陷報告
以下行為改變的缺陷報告被追溯地應用於以前釋出的 C 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
DR 445 | C11 | 型別可能具有擴充套件對齊,而無需涉及 _Alignas | 它必須具有基本對齊 |
[編輯] 參考
- C17 標準 (ISO/IEC 9899:2018)
- 3.15 物件 (p: 5)
- 6.2.6 型別表示 (p: 33-35)
- 6.2.8 物件的對齊 (p: 36-37)
- 6.5/6-7 表示式 (p: 55-56)
- C11 標準 (ISO/IEC 9899:2011)
- 3.15 物件 (p: 6)
- 6.2.6 型別表示 (p: 44-46)
- 6.2.8 物件的對齊 (p: 48-49)
- 6.5/6-7 表示式 (p: 77)
- C99 標準 (ISO/IEC 9899:1999)
- 3.2 對齊 (p: 3)
- 3.14 物件 (p: 5)
- 6.2.6 型別表示 (p: 37-39)
- 6.5/6-7 表示式 (p: 67-68)
- C89/C90 標準 (ISO/IEC 9899:1990)
- 1.6 術語定義
[編輯] 另請參閱
C++ 文件 關於 物件
|