使用通用函数将整数与短整数交换

Swapping an integer with a short using a generic function

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

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 的字节顺序不同,但显然这不会影响两个系统中的任何一个。

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

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

您可能会喜欢查看 Bit Twiddling Hacks 以了解 'generic' 进行交换操作的方法。

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

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

当您将字节从 short 复制到 int 时,您正在将 short 的值位、填充位和符号位复制到 int,但不一定保留位的含义。有点令人惊讶的是,该标准允许这样做,除非它不保证如果您的目标实现具有所谓的 "trap representations" 并且您不幸生成一个 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.

你在你的机器上找到的表示通常被概括为 "two's complement, little-endian",但是 C 标准提供了比描述所暗示的更多的表示灵活性(甚至允许一个字节有超过 8 位) ),这就是为什么可移植代码通常避免依赖整数类型的 bit/byte 表示。