与Do any compilers transfer effective type through memcpy/memmove
相关但有些不同在C89中,memcpy
和memmove
的行为就像使用字符类型访问源和目标一样,将源的所有位复制到目标,而不考虑数据类型被复制。
C99更改语义,以便如果将具有有效类型的对象复制到没有声明类型的存储[通常是从malloc或其他此类函数接收的存储],则会在目标存储中创建一个只能使用来源类型。
例如,以下代码将在C89上具有完全定义的行为 “unsigned int”和“unsigned long”具有相同的32位表示但在C99下具有未定义行为的所有平台。
#include <stdio.h>
#include <string.h>
void increment_32_bit_uint(void *p)
{
uint32_t temp;
memcpy(&temp, p, sizeof temp);
temp++;
memcpy(p, &temp, sizeof temp);
}
int main(void)
{
unsigned *ip = malloc(sizeof (unsigned));
unsigned long *lp = malloc(sizeof (unsigned long));
*ip = 3; *lp = 6;
increment_32_bit_uint(ip);
increment_32_bit_uint(lp);
printf("%u %lu", *ip, *lp);
return 0;
}
根据C99规则,将分配的存储传递给“increment_32_bit_uint”函数将使其将有效类型设置为uint32_t,即使所有三种类型都具有相同的类型,也不能与“unsigned”和“unsigned long”的类型相同表示。因此,编译器可以使用代码将任何它喜欢的内容作为uint32_t以外的类型读取,即使该类型具有相同的表示形式。
在C99或C11中,是否有任何方式以允许编译器生成有效代码的方式执行复制,但会强制编译器将目标视为包含无效的位模式类型[因此可以使用任何类型访问]?
答案 0 :(得分:1)
如果你只使用函数的返回类型,你可以摆脱所有有效的类型问题。
uint32_t increment_32_bit_uint (const void* p)
{
u32_t result;
memcpy(&result, p, sizeof(result));
result++;
return result;
}
这将强制调用者小心他们的类型。当然,理论上这是某种不可变对象,而不是变量的就地变化。但在实践中,如果您将其用作
,我认为您无论如何都会从中获得最有效的代码。x = increment_32_bit_uint(&x);
通常情况下,如果他们不将stdint.h
类型视为与其原始数据类型等效的兼容类型,我就不会看到任何严格的别名优化在实际应用中如何有用。特别是,它必须将uint8_t
视为字符类型,否则所有专业的低级C代码都会中断。
这里的情况也一样。如果编译器知道 unsigned int
是32位,为什么它会决定为uint32_t
的用户引起别名问题,反之亦然?这就是你如何编译无用的。