零大小数组未在结构内对齐
Zero size array is not aligned inside a structure
我发现在解压缩结构内声明时零大小数组的内存对齐有一个奇怪的行为。我突然想到用零大小的数组来标记结构的各个部分。我不认为它有什么特别的用途,但我仍然觉得可能会产生一些有趣的代码。
此行为是编译器特定的还是所有编译器都会给出相同的结果(它们不会对齐零大小数组)?
以下面的代码为例:
#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
Is this behaviour compiler specific or will all compilers give the same result (they will not align the zero size array)?
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 的扩展名为 zero length arrays。我认为 - 当然这是错误的。 char
成员在最后一个结构成员结束时开始,因此它“粘合”并指向前一个成员之后的下一个字节 - 因此它指向 (char*)&a.D + 1
,它可能等于也可能不等于下一个成员的地址,所以它取决于填充。在这种情况下,下一个成员是 int
,因此 char
和 int
之间需要一些填充。
我发现在解压缩结构内声明时零大小数组的内存对齐有一个奇怪的行为。我突然想到用零大小的数组来标记结构的各个部分。我不认为它有什么特别的用途,但我仍然觉得可能会产生一些有趣的代码。
此行为是编译器特定的还是所有编译器都会给出相同的结果(它们不会对齐零大小数组)?
以下面的代码为例:
#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
Is this behaviour compiler specific or will all compilers give the same result (they will not align the zero size array)?
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 的扩展名为 zero length arrays。我认为 - 当然这是错误的。 char
成员在最后一个结构成员结束时开始,因此它“粘合”并指向前一个成员之后的下一个字节 - 因此它指向 (char*)&a.D + 1
,它可能等于也可能不等于下一个成员的地址,所以它取决于填充。在这种情况下,下一个成员是 int
,因此 char
和 int
之间需要一些填充。