gpt4 book ai didi

c99 - 只要我们确定不创建别名,将 uint8_t* 转换为 uint32_t* 或 uint64_t* 是否在 C99 中定义良好?

转载 作者:行者123 更新时间:2023-12-03 16:47:40 25 4
gpt4 key购买 nike

考虑一个 C99 程序,它从通过链接器文件链接到程序二进制文件的只读二进制 blob 读取。程序知道 blob 在内存中的起始位置,但在编译期间不知道其布局。 blob 由无符号 32 位和 64 位整数组成。我们注意确保它们的字节序对应于所用平台上的(数据)字节序。我们还小心地将 blob 放入内存中,使其 4B 对齐。
要求:

  • (性能)我们希望根据各个平台的可能性(例如,在适用的情况下使用单个加载指令)以最少的指令数读取 32 位和 64 位整数
  • 我们不想逐字节读取值,然后使用移位和加法来重建 4B/8B 整数。

  • (便携)该程序必须在 ARM、x86_64 和 MIPS 架构上运行。还有一些体系结构有 32 位系统总线,其他体系结构有 64 位总线。
  • 我们不想使用内联汇编代码为每个架构维护特定于架构的改编。
  • 我们不想对使用过的工具链做出假设,例如我们不想使用 -fno-strict-aliasing和类似的。


  • 看起来,这可以通过类型双关来完成。我们知道我们想要读取的值在内存中的哪个位置,我们可以将指针从原始( unsigned char* )转换为 uint32_t* 之一。 , uint64_t* .
    但是 C99 严格的别名规则让我感到困惑。
    不会有别名,这一点我们可以肯定——我们不会在相同的内存位置将两种不同的类型混为一谈 unsigned char .二进制 blob 的布局不允许这样做。
    题:
    正在类型转换const uint8_t*const uint32_t* , 或 const uint64_t*在 C99 中定义良好,只要我们确定我们不会将相同的指针别名为 const uint32_t*const uint64_t* ?

    最佳答案

    严格的别名规则是有效的(双关语意为(第二个双关语也是如此))
    6.5p66.5p7 .
    如果您通读声明的字符缓冲区,例如:

    char buf[4096];
    //...
    read(fd, buf, sizeof(buf);
    //...
    想做 *(uint32_t*)(buf+position)那么你肯定违反了
    6.5p7

    An object shall have its stored value accessed only by an lvalueexpression that has one of the following types:

    • a type compatible with the effective type of the object,

    如果您 mmap 或 malloc 缓冲区(使内存动态类型化),那么它会更复杂,但无论如何,这是读取此类 uint32_t 的符合标准的方式。 --通过 memcpy -- 适用于任何一种情况并且通常不会带来性能损失,因为优化编译器识别 memcpy打电话并特别对待他们。
    例子:
    #include <stdint.h>
    #include <string.h>

    uint32_t get32_noalias(void const *P)
    {
    return *(uint32_t*)(P);
    }


    static inline uint32_t get32_inl(void const *P)
    {
    uint32_t const*p32 = P;
    //^optional (might not affect codegen)
    //to assert that P is well-aligned for uint32_t
    uint32_t x; memcpy(&x,p32,sizeof(x));
    return x;
    }

    //should generate same code as get32_noalias
    //but without violating 6.5p7 when P points to a char[] buffer
    uint32_t get32(void const *P)
    {
    return get32_inl(P);
    }
    https://gcc.godbolt.org/z/sGf4rf
    在 x86-64 上生成的程序集:
    get32_noalias:                          # @get32_noalias
    movl (%rdi), %eax
    retq

    get32: # @get32
    movl (%rdi), %eax
    retq
    虽然 *(uint32_t*)p在实践中可能不会在您的情况下爆炸(如果您只进行只读访问或只读访问与基于字符的写入交织在一起,就像 read 系统调用所做的那样,那么它“实际上”不应该爆炸),我没有理由避免完全符合标准 memcpy基于的解决方案。

    关于c99 - 只要我们确定不创建别名,将 uint8_t* 转换为 uint32_t* 或 uint64_t* 是否在 C99 中定义良好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64733632/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com