使用泛型函数交换带有short的整数

时间:2015-03-24 00:03:53

标签: c generics endianness memcpy

假设我有这个通用函数交换两个变量:

void swap(void *v1, void *v2, int size){
    char buffer[size];
    memcpy(buffer, v1, size);
    memcpy(v1, v2, size);
    memcpy(v2, buffer, size);
}

它工作正常,但我想知道在什么情况下这可能会破坏。我想到的一个案例是,当我们有两种不同的数据类型时,指定的大小不足以捕获更大的数据。例如:

int x = 4444;
short y = 5;
swap(&x, &y, sizeof(short));

我希望当我运行它时会产生不正确的结果,因为memcpy只能处理2个字节(而不是4个字节),并且部分数据在处理时会丢失或更改与x

令人惊讶的是,当我运行它时,它在我的Windows 7和Ubuntu操作系统上给出了正确的答案。我知道Ubuntu和Windows在字节序上有所不同,但显然不会影响这两个系统中的任何一个。

我想知道为什么泛型函数在这种情况下工作正常。

2 个答案:

答案 0 :(得分:2)

要完全理解这一点,您必须了解C标准以及您的机器和编译器的细节。从C标准开始,这里有一些相关的片段[我使用的标准是WG14 / N1256],总结了一点:

  • 有符号整数的对象表示由值位组成, 填充位和符号位。 [第6.2.6.2.2节]。
  • 这些位存储在连续的字节序列中。 [部分 6.2.6.1]。
  • 如果有N个值位,它们代表从2 ^ 0到2的2的幂 2 ^ {N - 1}。 [第6.2.6.2节]。
  • 符号位可以有三种含义之一,其中一种含义 具有值-2 ^ N(两个补码)[第6.2.6.2.2节]。

将字节从short复制到int时,您将short的值位,填充位和符号位复制到int的位{1}},但不一定保留位的含义。有点令人惊讶的是,标准允许这样做,除非它不能保证如果您的目标实现具有所谓的“陷阱表示”,那么您获得的int将是有效的。并且你不幸生成一个。

在实践中,您已经在您的计算机和编译器上找到了:

  • a short由2个字节表示,每个字节8位。
    • 符号位是第二个字节的第7位
    • 值的升序值是字节0的0-7位和字节1的0-6位。
    • 没有填充位
  • int由4个字节表示,每个字节8位。
    • 符号位是第四个字节的第7位
    • 值的升序值为字节0的0-7位,字节1的0-7,字节2的0-7和字节3的0-6。
    • 没有填充位

您还会发现两个表示都使用了两个补码。

在图片中(其中SS是符号位,数字N对应于值为2 ^ N的位):

short:
07-06-05-04-03-02-01-00 | SS-14-13-12-11-10-09-08

int:
07-06-05-04-03-02-01-00 | 15-14-13-12-11-10-09-08 | 23-22-21-20-19-18-17-16 | SS-30-29-28-27-26-25-24

您可以从中看到,如果将short的字节复制到零int的前两个字节,如果符号位为零,您将获得相同的值(也就是说,数字是正的)因为值位完全对应。作为必然结果,如果您从负值short开始,您也可以预测您将获得不同的值,因为short的符号位的值为-2 ^ 15但相应的int中的位值为2 ^ 15。

您在计算机上找到的表示通常被概括为"两个补码,小尾数",但C标准在表示中提供的灵活性比该描述更多建议(甚至允许一个字节超过8位),这就是为什么可移植代码通常避免依赖于整数类型的位/字节表示。

答案 1 :(得分:1)

正如评论中已经指出的那样,您使用的系统通常是little-endian(最低地址中的最低有效字节)。鉴于memcpy将short设置为int的最低部分。

您可能喜欢查看Bit Twiddling Hacks的“通用”方式来执行交换操作。