7

我有一个这样的数组:

unsigned char array[] = {'\xc0', '\x3f', '\x0e', '\x54', '\xe5', '\x20'};
unsigned char array2[6];

当我使用 memcpy 时:

memcpy(array2, array, 6);

并打印它们:

printf("%x %x %x %x %x %x", array[0],  // ... etc
printf("%x %x %x %x %x %x", array2[0], // ... etc

一个打印如下:

c0 3f e 54 e5 20

但另一个打印

ffffffc0 3f e 54 ffffffe5 20

发生了什么?

4

4 回答 4

13

我已经将您的代码变成了一个完整的可编译示例。我还添加了第三个“正常”数组,char它在我的环境中已签名。

#include <cstring>
#include <cstdio>

using std::memcpy;
using std::printf;

int main()
{

        unsigned char array[] = {'\xc0', '\x3f', '\x0e', '\x54', '\xe5', '\x20'};
        unsigned char array2[6];
        char array3[6];

        memcpy(array2, array, 6);
        memcpy(array3, array, 6);

        printf("%x %x %x %x %x %x\n", array[0], array[1], array[2], array[3], array[4], array[5]);
        printf("%x %x %x %x %x %x\n", array2[0], array2[1], array2[2], array2[3], array2[4], array2[5]);
        printf("%x %x %x %x %x %x\n", array3[0], array3[1], array3[2], array3[3], array3[4], array3[5]);

        return 0;
}

我的结果是我所期望的。

c0 3f e 54 e5 20
c0 3f e 54 e5 20
ffffffc0 3f e 54 ffffffe5 20

如您所见,只有当数组是有符号字符类型时,才会ff附加“额外”。原因是当memcpy填充有符号数组时char,设置了高位的值现在对应于负值char。当传递给printf被提升为char类型时int,这实际上意味着一个符号扩展。

%x以十六进制打印它们,就好像它们是 一样unsigned int,但是作为参数被传递,因为int行为在技术上是未定义的。通常在二进制补码机器上,行为与使用 mod 2^N 算术的标准有符号到无符号转换相同(其中 N 是 an 中的值位数unsigned int)。由于该值只是“略微”为负(来自窄带符号类型),因此转换后该值接近最大可能unsigned int值,即它有许多前导1(二进制)或f十六进制前导。

于 2010-08-18T14:00:28.837 回答
5

问题不是memcpy(除非您的 char 类型真的是 32 位,而不是 8 位),它看起来更像是打印时的整数符号扩展。

您可能希望更改您的 printf 以显式使用 unsigned char 转换,即。

printf("%hhx %hhx...", array2[0], array2[1],...);

作为猜测,您的编译器/优化器可能正在处理array(其大小和内容在编译时已知)并且array2不同的是,首先将常量值推入堆栈,然后错误地推入符号扩展值。

于 2010-08-18T13:55:50.397 回答
4

int您应该屏蔽较高位,因为在调用 varargs 函数时,您的字符将被扩展为大小:

printf("%x %x %x %x %x %x", array[0] & 0xff,  // ..
于 2010-08-18T13:56:17.880 回答
2

%x 格式需要整数类型。尝试使用铸造:

printf("%x %x %x %x %x %x", (int)array2[0], ...

编辑:由于我的帖子有新评论,我想添加一些信息。在调用 printf 函数之前,编译器会生成代码,该代码会推送参数的堆栈变量列表 (...)。编译器对 printf 格式代码一无所知,并根据其类型推送参数。printf 根据格式化字符串从栈中收集参数。因此,array[i] 被推送为 char,并由 printf 作为 int 处理。因此,如果参数类型与格式规范不完全匹配,则使用 printf/scanf 函数进行强制转换总是一个好主意。

于 2010-08-18T13:54:02.897 回答