char[] 大小未计算在内

Ham*_*frq 7 c gcc

我有以下代码:

#include <stdio.h>
#include <stdint.h>

typedef struct E_s {
    uint32_t    a;
    uint32_t    b;
    uint32_t    c;
} E_t;

typedef struct S_s {
    uint32_t    data_sz;
    char        data[];
} S_t;

typedef struct F_s {
    E_t     E;
    S_t     S;
    char        data[16];
//} __attribute__((packed)) full_msg_t;
} F_t;


int main(int argc, char* argv[])
{
    F_t out;
    printf("sizeof(out.data) = %lu\n", sizeof(out.data));
    printf("sizeof(out.E) = %lu\n", sizeof(E_t));
    printf("sizeof(out.S) = %lu\n", sizeof(S_t));
    printf("sizeof(out) = %lu\n", sizeof(F_t));

    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

当我运行代码时,我看到以下输出:

sizeof(out.data) = 16
sizeof(out.E) = 12
sizeof(out.S) = 4
sizeof(out) = 32
Run Code Online (Sandbox Code Playgroud)

问题:为什么大小是S_t4(输出的第三行)?我原以为是 8 ( uint32_t+ char[])。为什么不包括尺寸char[]

此外,两者out.dataout.S.data指向相同的内存位置,这使我深入研究并发现了上述观察结果。这里的任何线索也会非常有帮助。我没想到这两个变量会重叠。

Jon*_*ler 8

标准规定,在计算大小时,具有灵活数组成员 (FAM)的结构体的变量部分将被忽略:

\n
\n

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这称为灵活数组成员。在大多数情况下,灵活数组成员会被忽略。特别是,该结构的大小就像省略了柔性阵列成员一样,只是它可能具有比省略所暗示的更多的尾部填充。但是,当.(或->)运算符的左操作数是(指向)具有灵活数组成员的结构并且右操作数命名该成员时,其行为就像该成员被最长的数组(具有相同的数组)替换。元素类型)不会使结构大于正在访问的对象;阵列的偏移应保持灵活阵列成员的偏移,即使这与替换阵列的偏移不同。如果此数组没有元素,则它的行为就好像它有一个元素一样,但如果尝试访问该元素或生成一个超过该元素的指针,则该行为是未定义的。

\n
\n

添加了强调

\n

请注意,不应接受struct F_s(aka );违反了\xc2\xa76.7.2.1 \xc2\xb63F_t中的约束:

\n
\n

结构或联合不应包含不完整或函数类型的成员(因此,结构不应包含其自身的实例,但可以包含指向其自身实例的指针),除非结构的最后一个成员具有超过一个命名成员可能具有不完整的数组类型;此类结构(以及可能递归地包含属于此类结构的成员的任何联合)不应是结构的成员或数组的元素。

\n
\n

编译器应该拒绝(或者至少发出诊断),因为违反约束需要诊断。即使编译器没有完全拒绝它,您实际上也不能使用嵌入的 FAM,S_t因为data的成员F_t不会移动 \xe2\x80\x94 结构体元素的偏移量固定为编译时间。事实上,它会使用data的元素F_t,但这不是定义的行为。

\n


dbu*_*ush 8

在此结构中:

typedef struct S_s {
    uint32_t    data_sz;
    char        data[];
} S_t;
Run Code Online (Sandbox Code Playgroud)

data成员是灵活的阵列成员。此类成员不会影响结构的大小,因为未指定其大小。C 标准第 6.7.2.1p18 节对此进行了详细说明:

作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这称为灵活数组成员。在大多数情况下,灵活数组成员会被忽略。特别是,该结构的大小就像省略了柔性阵列成员一样,只是它可能具有比省略所暗示的更多的尾部填充。

所以 的大小S_t不包括data成员,这就是为什么sizeof(S_t)是 4。

仅当动态分配结构体的内存时才能使用此类成员。例如:

S_t *s = malloc(sizeof(S_t) + 10);
Run Code Online (Sandbox Code Playgroud)

这允许您访问从s->data[0]s->data[9]

这也意味着您不能将具有灵活数组成员的结构放入另一个结构内部或数组中,因为无法确切知道灵活数组成员的结束位置。

第 6.7.2.1p3 节对此进行了详细说明:

结构或联合不应包含不完整或函数类型的成员(因此,结构不应包含其自身的实例,但可以包含指向其自身实例的指针),除非结构的最后一个成员具有超过一个命名成员可能具有不完整的数组类型;此类结构(以及可能递归地包含属于此类结构的成员的任何联合)不应是结构的成员或数组的元素


Lun*_*din 7

char data[];是一个灵活的数组成员,并且明确保证不计算其大小。因为它主要应该用作malloc(sizeof(St_t) + n),其中n是数组的大小data

至于S_t S;另一个结构内部,这是无效的 C,因为包含灵活数组成员的结构必须放置在末尾和最外层结构中,而您没有这样做。因此,您的代码不能在标准 C 中编译,并且不可能假设 和out.S.dataout.data相同的内存,因为所有这些都超出了 C 语言的范围。我想 GNU C 有可能以非标准扩展的形式提供确定性行为,但我不知道有任何这样的保证。