繁体   English   中英

使用指向 C/C++ 中未对齐的 memory 地址的指针访问结构是否安全?

[英]Is it safe to access a struct using pointer pointed to unaligned memory address in C/C++?

struct CustomData
{
    char flag;
    int count;
    double value;
};

CustomData custom_data{};

char *buf = new char[sizeof(CustomData) + 3];
memcpy(buf + 3, &custom_data, sizeof(CustomData));

CustomData* ptr = (CustomData *)(buf + 3);

ptr->count = 10;
ptr->value = 20.0;

我有一个 memory 缓冲区,由一些 header 字节和一个结构组成。 该结构是此 memory 缓冲区的 memcpy。 我必须修改这个嵌入结构中的一些字段,如上所示。

我的问题是:

  1. 这是 x86-64 上的安全访问吗? 其他平台呢?

  2. memcpy到本地结构,修改并复制回来肯定可以,但似乎很浪费。 有没有办法检查ptr是否正确对齐以安全访问结构?

Neither the C nor the C++ standards define the behavior of accessing memory without the alignment required for the lvalue type, nor even of converting a pointer to another pointer type when the pointer value does not have the alignment required for the destination type. 问题不在于您的目标架构(x86-64 或其他)是否支持未对齐访问,而在于您的 C 或 C++ 实现,尤其是编译器是否支持它。

查看您的示例代码,我怀疑它是将数据从网络或文件或其他来源复制到缓冲区中的代理,然后尝试将部分数据解释为所需的类型,可能是在检查收到的数据中的 header 之后确定主题数据的类型和/或位置。 如果是这样,你应该描述一下原来的情况,因为你显示的代理代码是不够的。

从网络或文件中以字节形式读取原始数据时,最好将数据直接读取到所需的结构中。 理想情况下,将指定通信协议以根据需要对齐数据,以便一旦将数据接收到对齐的缓冲区中就不需要进一步的操作。 在 C 中,联合可能有助于处理将一种类型的数据混叠为另一种类型的问题。

如果直接进入结构方法失败,则可以使用memcpy ,它可能不会像您想象的那样低效,因为memcpy方法会生成具有明确行为的代码,然后编译器通常可以,只要对相关部分有足够的可见性代码,优化它生成的程序集,以便不会发生实际的memcpy

Failing that, some compilers have various extensions to the C and C++ standards that let you define structure types without alignment requirements and to access memory using arbitrary types (supporting aliasing character arrays as other types).

至少在 C 中,动态分配的 memory 可以使用字符类型填充数据,然后按照有关“有效类型”的规则作为另一种类型访问。 但是,必须遵循有关别名的某些规则,这不能访问不正确对齐的数据(不使用上述扩展)。

任何指针双关都是危险的,因为它可能违反严格的别名规则。

您需要使用memcpy 大多数优化编译器都非常了解memcpy并且通常不会发出对memcpy的实际调用:

typedef struct CustomData
{
    char flag;
    int count;
    double value;
}CustomData;


CustomData *foo(int val, double d)
{
    char *buf = new char[sizeof(CustomData) + 3];

    CustomData cd;

    cd.count = val;
    cd.value = d;

    memcpy(buf + 3, &cd, sizeof(cd));
    return (CustomData *)buf;
}
foo(int, double):
        push    rbp
        mov     ebp, edi
        mov     edi, 19
        push    rbx
        sal     rbp, 32
        movq    rbx, xmm0
        sub     rsp, 8
        call    operator new[](unsigned long)
        mov     QWORD PTR [rax+3], rbp
        mov     QWORD PTR [rax+11], rbx
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret

有些操作系统有 memory alignment 要求,如果我没记错的话,Solaris 要求 memory 在 8 字节边界上对齐。 如果不满足此要求,操作系统将抛出错误

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM