【问题标题】:memcpy adds ff ff ff to the beginning of a bytememcpy 将 ff ff ff 添加到字节的开头
【发布时间】:2011-03-31 13:50:28
【问题描述】:

我有一个这样的数组:

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

发生了什么?

【问题讨论】:

  • 你能仔细检查array2的定义吗?
  • 它似乎将 array2 视为有符号整数而不是无符号字符。
  • 我也刚刚检查过,如果我做 array2[0] = array[0] 等,它会做同样的事情。
  • 只是无法确认您所看到的内容。
  • 你用的是什么编译器?与 gcc4.2 两行给出相同的输出

标签: c++ memcpy


【解决方案1】:

我已将您的代码变成了一个完整的可编译示例。我还添加了第三个“普通”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 是 unsigned int 中的值位数)。由于该值仅“略微”为负(来自窄符号类型),转换后该值接近最大可能的unsigned int 值,即它有许多前导1(二进制)或前导@ 987654337@ 十六进制。

【讨论】:

  • 您总是必须小心使用打印样式语句检查内存的值 - 最好使用调试器
【解决方案2】:

%x 格式需要整数类型。尝试使用强制转换:

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

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

【讨论】:

  • char 是整数类型。 C99 标准:6.2.5 第 4 段:There are five standard signed integer types, designated as signed char, short int, int, long int, and long long int. (These and other types may be designated in several additional ways, as described in 6.7.2.)
  • 但是 char 可能不会以四个字节在堆栈上传递,这是 %x 所期望的。
  • 函数调用会将 char 提升为 int(并位填充现有提升)
  • 为什么会有任何不同?它只是进行与 varargs 函数参数的提升相同的转换,无论如何只会使其显式。
  • @Osgx, @Mark B: %x 需要一个 int 类型。当作为参数传递时,char 将始终扩展为 int。问题在于扩展的符号扩展。
【解决方案3】:

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

printf("%x %x %x %x %x %x", array[0] & 0xff,  // ..

【讨论】:

  • @osgx - 是的,但是我们这些老 C 很久以前就学会了 & 0xff :) 旧习惯很难改掉
  • %hhx 还不是 C++,请记住 C++ 指的是其 printf 合约的 C99 之前的标准版本。
  • @Charles Bailey,但没有用于 C++ 的 libc 库,任何 C++ prog 都将使用 C 中的 libc。因此,在最近(抱歉,不是古代)libc hhx 将得到支持。跨度>
  • @osgx:请检查您的标准,特别是 1.2 规范性参考。在 C++ 标准中,作为 标准 C 库 被称为(并且在 C++ 中可用)的是 ISO/IEC 9899:1990 和 ISO/IEC 9899/Amd.1:1995 的第 7 条,即 C90标准库。
  • @osgx:我不确定我是否理解你在说什么。如果您使用的是符合标准的 C++ 实现,则只能依靠 C90 Standard C Library 可用。您不能指望能够使用 C99 功能。
【解决方案4】:

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

您可能希望更改您的 printf 以明确使用无符号字符转换,即。

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

作为猜测,您的编译器/优化器可能以不同方式处理 array(其大小和内容在编译时已知)和 array2,首先将常量值压入堆栈并错误地压入符号第二个扩展值。

【讨论】:

    猜你喜欢
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 2011-04-20
    • 2011-12-11
    • 1970-01-01
    相关资源
    最近更新 更多