簡體   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