结构对齐安全使用

Struct alignment safe use

我是一家公司的新员工,该公司使用了以下结构:

#include <stdio.h>
#include <string.h>

typedef unsigned char uint8;
typedef signed char int8;
typedef unsigned short int uint16;
typedef signed short int int16;

typedef struct padded_t {
    int8 array0[11];
    int8 array1[4];
    uint16 len2;
    int8 array2[25];
    uint16 len3;
    int8 array3[6];
    // many more len/arrays follow
} padded_t;

int main(int argc, char** argv)
{
    padded_t foo;
    memset((void*)&foo, 0, sizeof(padded_t));

    int8* str = "foobar";
    int16 size = (int16)strlen(str);

    int8* ptr = (int8*)&foo.len2;

    // please note that the memcpy references only the pointer to len
    // are the following couple of lines safe?
    memcpy ((void*)ptr, &size, 2);
    memcpy ((void*)&ptr[2], (void*)str, size);

    printf("%d\n", foo.len2);
    printf("%s\n", foo.array2);

    return 0;
}

我知道一些关于对齐和填充的事情,我假设编译器(ARM9 设备的 gnu C99)会添加一些填充以使结构对齐。

但是这段代码安全吗?
据我了解,只要 uint16 len 变量紧跟 int8 array[] 变量,无论其他结构成员如何,它都是安全的。

当 uint16 之前的大小为奇数时,它会只在 uint16 之前添加填充吗?
这种用法正确吗?更重要的是,它安全吗?

您不需要编写适用于每个系统的代码。您应该做的是编写具有可预测行为的代码。它要么按设计工作,要么如果您的假设不成立,静态断言会中止编译。

第二个 memcpy 行没有做到这一点。它假定 offsetof(struct padded_t, len2) + 2 == offsetof(struct padded_t, array2)。一个经常成立但完全愚蠢的假设。

为什么不写

foo.len2 = strlen(str);
memcpy (foo.array2, str, foo.len2);
//possibly, foo.array2[foo.len2] = '[=10=]';

代码可读。没有不需要的变量。没有不必要的演员表。没有不可预测的行为。 原始代码看起来不像你会从中学到很多东西,混乱看起来不像我希望精通 C 的人写的东西。

回答您的评论,将它们打包是错误的解决方法。因为它会使成员错位并且只会打开 another can of worms.

还要警惕自定义固定大小的 typedef。我曾经有过在具有 chars unsigned

的系统上调试 typedef char int8; 的乐趣

But is this code safe?

As I understand it, it will be safe as long as the uint16 len variables are immediately followed by int8 array[] variables regardless of the other struct members.

从允许编译器在结构成员之间或之后插入任意数量的填充的意义上讲,这是不安全的,因此您不能确信 &ptr[2] 指向 [=14 的第一个字节=].但是,假设 uint16 确实是两个字节宽(语言无法保证这一点),您可以确信如果 size 小于 25,则

memcpy ((void*)&ptr[2], (void*)str, size);

既不会修改 foo.len2 的任何字节,也不会修改 foo.array2 的最后一个字节。由于 foo 之前用零填充,这将使 foo.array2 保留为正确终止的 C 字符串。因此用 printf() 打印它是安全的。另一方面,C 不保证这样做的结果与打印 str.

的结果相同

Will it only add paddings before a uint16 when the size before it is odd?

这是编译器的决定。它可能会受到编译指示、命令行选项、配置选项、语言扩展(尽管示例中使用了 none)、目标体系结构或编译器想要用来做出此类决定的任何其他因素的影响.

Is this use correct?

据我所知,该程序是符合标准的,如果这是你的意思的话。

And more importantly, is it safe?

仅从其代码无法预测该程序的输出,因此从这个意义上说,不,它不安全。