【问题标题】:Is it legal to consume void * buffers as anything other than char *?将 void * 缓冲区用作 char * 以外的任何内容是否合法?
【发布时间】:2017-04-30 04:57:19
【问题描述】:

假设我有一个来自某个外部来源的已知大小的void *buffer(例如,它可能是fread() C API[1],或mmap 调用) .

我可以有效地将这个void * 转换为什么类型的指针,然后从中读取?

如果我知道此数据由 16 位值组成,是否允许将 void * 转换为 uint16_t * 并通过取消引用指针直接读取值?

我知道当然可能存在字节顺序问题,但首先这样做是否合法(例如,对齐怎么样)?

如果以这种方式强制转换整个缓冲区是合法的,那么缓冲区的一部分呢?例如,如果我知道前 64 个字节是 char *,然后接下来的 10,000 个字节是 uint16_t 数据?

[1] 在fread() 的情况下,假设内存是用malloc 分配的。

【问题讨论】:

    标签: c io language-lawyer strict-aliasing


    【解决方案1】:

    有两个可能的问题:

    1. 演员可能会受到对齐限制。
    2. 通过转换结果读取或写入受严格的别名规则约束。

    对于第 1 部分,平台是否具有对齐要求由实现定义。查阅编译器文档,它必须说明是否存在此类限制。如果是这样,那么如果您转换的指针未正确对齐转换目标所指向的类型,则这是未定义的行为。

    对于第 2 部分,您需要了解严格的别名规则。 See this thread 获取标准报价以及各种形式的介绍。

    我从这里开始的回答仅指在动态分配的空间中工作。如果通过不同类型读取和写入数据,其中不允许读取的类型为写入的类型起别名,则会出现问题:

    uint16_t *buf = malloc(50);
    ((char *)buf)[0] = 'a';
    ((char *)buf)[1] = 'b';
    *buf;  // undefined behaviour
    

    所以要回答您的问题,您需要知道数据是如何写入的。

    fread 的情况下,标准 (C11 7.21.8.1/2) 指定它在写入时就像对 unsigned char 字符进行了一系列赋值。因此,将 fread 放入 malloc 的缓冲区然后通过 uint16_t 表达式读取将是未定义的行为。

    mmap 函数不是 C 标准的一部分。因此,该标准没有涵盖如果您在写入之前读出mmap'd 空间会发生什么。但是我想说,如果你写入这样的空间,然后从同一个地址读取,那么严格的别名规则就会适用。


    一些编译器具有“禁用严格别名”的开关或编译指示,这意味着它们将编译代码,就好像所有别名都被允许一样。如果您想使用违反严格别名规则的编码技术,那么最好为该代码使用此类开关。

    【讨论】:

    • 是的,对于 mmapread(2) 等所有“不是 C 但大量使用 C”API 和朋友来说,那里似乎有一个大漏洞。关于严格别名的规则与写入的类型有关,AFAIK,但认为它会跨越进程边界是很奇怪的(事实上,写入可能甚至没有发生在 C 或当前主机上等)。
    • @SODIMM 实际上,编译器必须将来自未知来源的写入视为某种特定类型,但它可以假设它仍然是一种类型,例如如果您读取与 int 和 float 相同的 mmap'd 字节,您仍然会遇到麻烦,因为编译器知道它最多只能写为一个或另一个
    【解决方案2】:

    “合法” - 如果你的意思是你能做到,答案是肯定的。它是否正常工作取决于你做什么。

    如果您确定您在属于您的内存空间范围内操作,您可以将void * 转换为uint16 * 或其他任何内容。

    此类操作经常在视频、压缩等的高速代码中完成。

    如果不需要零复制速度,更安全的方法是简单地在堆栈上分配该类型,然后使用memcpy 或赋值将其复制进去以修复对齐。

    从 Linux 内核中查看这些对齐宏模式,它们基本上是这样做的(如果值已经对齐,编译器可能会对此进行优化):align macro kernel

    【讨论】:

    • "cast void * to a uint16 *" 当然会破坏对齐要求。
    • 这就是宏的用途。编译器会自动修正对齐方式。
    猜你喜欢
    • 2011-12-24
    • 2011-04-18
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    相关资源
    最近更新 更多