假设我的某个外部源的已知大小为void *buffer
(例如,它可能是fread()
C API [1],或者是{ {1}}致电)。
我可以有效地将此mmap
投射到哪些类型的指针,然后从中读取?
如果我知道这些数据是由16位值组成的,那么是否允许将void *
转换为void *
并直接通过取消引用指针来读取值?
我知道当然存在可能的字节序问题,但首先这样做是否合法(例如,对齐方式)?
如果以这种方式强制转换整个缓冲区是合法的,那么缓冲区的一部分呢?例如,如果我知道前64个字节是uint16_t *
,那么接下来的10,000个字节是char *
数据?
[1]在uint16_t
的情况下,假设内存分配有fread()
。
答案 0 :(得分: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
'空格之前读出会发生什么。但我会说,如果你写入这样的空间,然后从同一地址读取,那么严格的别名规则将适用。
某些编译器具有“禁用严格别名”的开关或编译指示,这意味着它们将编译代码,就好像所有别名都被允许一样。如果您想使用违反严格别名规则的编码技术,那么最好将这些开关用于该代码。
答案 1 :(得分:0)
通过" legal" - 如果你的意思是你可以做到,答案是肯定的。它是否正常工作取决于你的工作。
如果您确定自己在自己的记忆空间范围内操作,可以将void *
投射到uint16 *
或其他任何内容。
这种操作经常在用于视频,压缩等的高速代码中完成。
如果不需要零复制速度,更安全的方法是简单地在堆栈上分配该类型,然后使用memcpy
或赋值将其复制以修复对齐。
请参阅Linux内核中的这些对齐宏模式,基本上这样做(如果值已经对齐,编译器可能会对此进行优化):align macro kernel