c99

时间:2016-03-11 23:09:34

标签: c c99 memcpy c11 strict-aliasing

Do any compilers transfer effective type through memcpy/memmove

相关但有些不同

在C89中,memcpymemmove的行为就像使用字符类型访问源和目标一样,将源的所有位复制到目标,而不考虑数据类型被复制。

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中,是否有任何方式以允许编译器生成有效代码的方式执行复制,但会强制编译器将目标视为包含无效的位模式类型[因此可以使用任何类型访问]?

1 个答案:

答案 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的用户引起别名问题,反之亦然?这就是你如何编译无用的