首页 > 解决方案 > 零大小数组在结构内未对齐

问题描述

当在未打包的结构中声明时,我发现关于零大小数组的内存对齐的奇怪行为。我突然想到抛出零大小的数组来标记结构的某些部分。我不认为它有什么特别的用途,但我仍然觉得可能会产生一些有趣的代码。

这种行为是编译器特定的还是所有编译器都会给出相同的结果(它们不会对齐零大小的数组)


以下面的代码为例:

#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

标签: cgccstructmemory-alignment

解决方案


这种行为是编译器特定的还是所有编译器都会给出相同的结果(它们不会对齐零大小的数组)?

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之间需要一些填充。charint


推荐阅读