linux - ARM 上未对齐内存访问的函数

标签 linux gcc arm undefined-behavior memory-alignment

我正在开发一个从内存中读取数据的项目。其中一些数据是整数,在未对齐的地址访问它们时出现问题。我的想法是使用 memcpy 来实现,即

uint32_t readU32(const void* ptr)
{
    uint32_t n;
    memcpy(&n, ptr, sizeof(n));
    return n;
}

我从项目源代码中找到的解决方案与此代码类似:

uint32_t readU32(const uint32_t* ptr)
{
    union {
        uint32_t n;
        char data[4];
    } tmp;
    const char* cp=(const char*)ptr;
    tmp.data[0] = *cp++;
    tmp.data[1] = *cp++;
    tmp.data[2] = *cp++;
    tmp.data[3] = *cp;
    return tmp.n;
}

所以我的问题:

  1. 第二个版本不是未定义的行为吗? C 标准在 6.2.3.2 指针第 7 节中说:

A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned 57) for the pointed-to type, the behavior is undefined.

由于调用代码在某些时候使用了 char*要处理内存,必须对 char* 进行一些转换至uint32_t* 。那么,如果 uint32_t* 这不是未定义行为的结果吗?没有正确对齐?如果,那么该函数就没有意义,您可以写 *(uint32_t*)来获取内存。另外,我想我在某处读到编译器可能期望 int*正确对齐和任何未对齐的int*也意味着未定义的行为,因此该函数的生成代码可能会产生一些快捷方式,因为它可能期望函数参数正确对齐。

  • 原代码有volatile在参数和所有变量上,因为内存内容可能会改变(它是驱动程序内的数据缓冲区(无寄存器))。也许这就是为什么它不使用memcpy,因为它不适用于 volatile 数据。但是,这在哪个世界才有意义呢?如果基础数据随时可能发生变化,那么一切都将失败。数据甚至可能在这些字节复制操作之间发生变化。因此,您必须有某种互斥体来同步对此数据的访问。但如果你有这样的同步,为什么还需要 volatile?

  • 是否有针对此内存访问问题的规范/接受/更好的解决方案?经过一番搜索后,我得出的结论是,您需要互斥锁,不需要 volatile ,可以使用 memcpy .

  • 附注:

    # cat /proc/cpuinfo
    processor       : 0
    model name      : ARMv7 Processor rev 10 (v7l)
    BogoMIPS        : 1581.05
    Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls
    CPU implementer : 0x41
    CPU architecture: 7
    CPU variant     : 0x2
    CPU part        : 0xc09
    CPU revision    : 10
    

    最佳答案

    这段代码

    uint32_t readU32(const uint32_t* ptr)
    {
        union {
            uint32_t n;
            char data[4];
        } tmp;
        const char* cp=(const char*)ptr;
        tmp.data[0] = *cp++;
        tmp.data[1] = *cp++;
        tmp.data[2] = *cp++;
        tmp.data[3] = *cp;
        return tmp.n;
    }
    

    将指针作为uint32_t *传递。如果它实际上不是 uint32_t,那就是 UB。该参数可能应该是 const void *

    在转换本身中使用 const char * 并不是未定义的行为。根据 the C Standard6.3.2.3 指针第 7 段(强调我的):

    A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.
    Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

    对于直接在特定硬件上访问内存/寄存器的正确方法,使用 volatile 将没有规范/可接受/最佳的解决方案。任何解决方案都特定于您的系统并且超出了标准 C 的范围。

    关于linux - ARM 上未对齐内存访问的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45506130/

    相关文章:

    c - 在 Weston 下运行嵌入在 GTK 窗口中的 Gstreamer 视频

    c++ - gcc 插件 c c++ 查找全局非静态变量

    我们可以将 bzip2 block 大小减少到 100KB 以下吗?

    php - 在运行的 Plesk 11.5 服务器上更新 PHP/MySQL 的正确方法?

    c - 尝试将 GCC 特定的 asm goto 移植到 Clang

    c++ - 如何将工具链交叉编译到 ARMv5 平台?

    c++ - 最终使用 NEON Copy 但不使用 memcpy 的 ARM Linux 内存碎片

    c - __atomic_test_and_set 与 __atomic_compare_exchange_n

    objective-c - 我们什么时候应该在 Objective-C 方法中使用 "__attribute__((unused))"?

    c - 为什么指针算法在架构之间不一致?