ARM

时间:2017-08-04 11:51:13

标签: 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:
  2. 表示
      

    指向对象或不完整类型的指针可能会转换为指向其他对象的指针   对象或不完整的类型。如果结果指针没有正确对齐57)   指向类型,行为未定义。

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

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

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

    3. P.S:

      # 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
      

2 个答案:

答案 0 :(得分:2)

This code

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;
}

passes the pointer as a uint32_t *. If it's not actually a uint32_t, that's UB. The argument should probably be a const void *.

The use of a const char * in the conversion itself is not undefined behavior. Per 6.3.2.3 Pointers, paragraph 7 of the C Standard (emphasis mine):

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.

The use of volatile with respect to the correct way to access memory/registers directly on your particular hardware would have no canonical/accepted/best solution. Any solution for that would be specific to your system and beyond the scope of standard C.

答案 1 :(得分:1)

允许实现在标准没有的情况下定义行为,并且一些实现可以指定所有指针类型具有相同的表示并且可以在彼此之间自由地转换而不管对齐,只要实际使用的指针< em>访问事物是适当对齐的。

不幸的是,因为一些迟钝的编译器强迫使用“memcpy”作为 即使已知指针对齐,也会出现混叠问题的逃逸阀, 编译器可以有效处理需要编写的代码的唯一方法 对齐存储的类型不可知访问是假设任何需要对齐的类型的指针总是适合于这种类型。因此,您使用uint32_t*接近的直觉很危险。可能需要进行编译时检查以确保函数是传递void *或uint32_t *,而不是像uint16_t *或double *那样的东西,但是没有办法在不允许的情况下声明函数通过将字节访问合并到32位加载中来“优化”该函数的编译器,如果指针未对齐,该加载将失败。