當在解壓結構中宣告時,我發現了關于零大小陣列的記憶體對齊的奇怪行為。我突然想到要拋出零大小的陣列來標記結構的各個部分。我不認為它有特殊用途,但我仍然覺得可能會產生一些有趣的代碼。
這種行為是編譯器特定的還是所有編譯器都會給出相同的結果(它們不會對齊零大小陣列)?
以下面的代碼為例:
#include <stdio.h>
struct tagOBJ
{
char Label1[0];
int A;
char Label2[0];
int B;
int C;
};
typedef struct tagOBJ OBJ;
int main()
{
OBJ a;
printf("%d == %d\n", &a.A, a.Label1);
printf("%d == %d\n", &a.B, a.Label2);
return 0;
}
在上述情況下,輸出符合預期:
6422292 == 6422292
6422296 == 6422296
但是,如果我將另一個變數添加到暗示填充的結構中,則給定的地址是不同的:
struct tagOBJ
{
char Label1[0];
int A;
char D;
char Label2[0];
int B;
int C;
};
typedef struct tagOBJ OBJ;
生產
6422288 == 6422288
6422296 == 6422293 // ! false
uj5u.com熱心網友回復:
這種行為是編譯器特定的還是所有編譯器都會給出相同的結果(它們不會對齊零大小陣列)?
C 語言本身不允許大小為零的陣列。您的代碼無效并且您的代碼的行為是"undefined"。任何編譯器都可以做任何事情,所以,答案是肯定的,它是特定于編譯器的,并且也沒有要求行為無論如何都應該一致。
還:
printf("%d == %d\n", &a.A, a.Label1);
也是無效的。%d期望一個int-&a.A是一個指標。
本著 C 的精神,添加訪問器函式會更好:
void *OBJ_public(const OBJ *t) { return (void*)&t->A; }
void *OBJ_private(const OBJ *t) { return (void*)&t->C; }
如果你真的想使用宏,那么你可以這樣做:
#define OBJ_PUBLIC A
#define OBJ_PRIVATE C
#define PUBLIC(TYPE, VAR) (void*)(&(VAR).TYPE##_PUBLIC)
#define PRIVATE(TYPE, VAR) (void*)(&(VAR).TYPE##_PRIVATE)
int main() {
OBJ a;
printf("%p\n", PUBLIC(OBJ, a));
}
6422296 == 6422293 // ! false
Gcc 具有零長度陣列的擴展。我認為 - 肯定是假的。charmember 在最后一個 struct 成員結束時開始,因此它“粘合”并指向前一個成員之后的下一個位元組 - 所以它指向(char*)&a.D 1,它可能等于也可能不等于下一個成員的地址,所以這取決于在填充上。在這種情況下int,下一個成員是,因此在char和之間需要一些填充int。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/356308.html
上一篇:評估/列印用作宏輸入的函式回傳值
