假设我有这个通用函数交换两个变量:
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在字节序上有所不同,但显然不会影响这两个系统中的任何一个。
我想知道为什么泛型函数在这种情况下工作正常。
答案 0 :(得分:2)
要完全理解这一点,您必须了解C标准以及您的机器和编译器的细节。从C标准开始,这里有一些相关的片段[我使用的标准是WG14 / N1256],总结了一点:
将字节从short
复制到int
时,您将short
的值位,填充位和符号位复制到int
的位{1}},但不一定保留位的含义。有点令人惊讶的是,标准允许这样做,除非它不能保证如果您的目标实现具有所谓的“陷阱表示”,那么您获得的int
将是有效的。并且你不幸生成一个。
在实践中,您已经在您的计算机和编译器上找到了:
short
由2个字节表示,每个字节8位。
int
由4个字节表示,每个字节8位。
您还会发现两个表示都使用了两个补码。
在图片中(其中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的“通用”方式来执行交换操作。