【问题标题】:Copying integer to char buffer in C将整数复制到C中的char缓冲区
【发布时间】:2015-08-03 14:39:57
【问题描述】:
  uint32_t after = 0xe1ca95ee;
  char new_buf[4];
  memcpy(new_buf, &after, 4);
  printf("%x\n", *new_buf); // I want to print the content of new_buf

我想将after的内容复制到new_buf。但结果令人困惑。 printf 给了我ffffffee。它看起来像一个地址。我已经取消引用new_buf

根据 cmets,我不能使用 memcpystrncpy 来执行此任务。但为什么? memcpystrncpy 仅设计用于处理 char *?但是after的内容在内存中。

PS:我知道我应该使用sprintfsnprintf。如果你能解释为什么memcpystrncpy 不适合这种情况,我很感激。

【问题讨论】:

  • 你确定要这样做memcpy()
  • 您希望打印什么?您可以尝试完成几件不同的事情,但我不知道是哪件。
  • 直到代码需要“将整数复制到字符缓冲区”的更大问题之前,答案仍然很短。将uint32_t after 复制到uint8_t new_buf8[4]; 是有道理的,但复制到char [] 暗示了一个更大的编码目标的开始,这是有问题的。

标签: c


【解决方案1】:

这就是问题所在:

printf("%x\n", *new_buf);

这为您提供了您所要求的:它使用 %x 格式在 new_buf 位置打印 char。该位置已经包含 0xee(在成功的 memcpy 之后,首先是最低有效字节,因为您很可能在 Intel 机器上,小端),但它被打印为 0xffffffee(负数),因为它是一个 char 而不是 unsigned char,因为当然 0xee 是有符号字节(> 0x7F,最高位设置)

你应该改用:

printf("%x\n", *((unsigned int*)new_buf));

...在下面编辑...

或者更确切地说:

printf("%x\n", *((uint32_t*)new_buf));

【讨论】:

  • 另外,请注意字节序。
  • 由于指针别名规则的不对称性,您建议的两个构造都有未定义的行为。 (您可以通过具有字符类型的指针访问任何类型的对象,但您可以通过具有非字符类型的指针访问声明为具有字符类型的对象。)
  • @zwol 但我们没有访问字符类型的对象。我们正在访问char[4] 类型的对象。
  • @PSkocik "Character type" 是 C 标准中的一个艺术术语,它包含单个 char 对象和它们的数组(可能还有一些我不记得的其他内容)我的头顶)。无论如何,重点是char[4]int 不是兼容的类型,因此您不能使用int* 访问声明为char[4] 的内存,即使可以 i> 使用char* 访问声明为int 的内存。 char* 有一个特殊的例外,但它只向一个方向发展。
  • @zwol 当然它是未定义的。它是未定义的,因为它取决于整数的位表示(即,大端,小端,...)。但是你有一个 http 类型的指针来解释为什么它应该是未定义的吗?
【解决方案2】:

如果你这样做:

int i;
for (i = 0; i < 4; i++) printf("%x\n", new_buf[i]);

你可以看到它打印出来

ffffffee
ffffff95
ffffffca
ffffffe1

所以你的字节都在那里。正如@George André 所指出的,它们是有符号字节,所以你看到fs 被填充到前面,因为数字是负数并且它总是打印4 个字节,ee 以32 位表示是ffffffee。您可能在一个小端机器上,因此最低有效字节 ee 实际上存储在最低内存位置,这就是为什么在取消引用 new_buf 时会获得数字的“最后一个”字节。其他部分已被他人回答,打印时必须将new_buf声明为未签名或强制转换。

   uint32_t after = 0xe1ca95ee;
   char new_buf[4];
   memcpy(new_buf, &after, 4);
   printf("%x\n", *((unsigned char*)new_buf));

或者

   uint32_t after = 0xe1ca95ee;
   unsinged char new_buf[4];
   memcpy(new_buf, &after, 4);
   printf("%x\n", *new_buf);

【讨论】:

  • 这个答案是错误的,他想在 new_buf 位置打印出 uint32_t。此外,“那些”来自 new_buf 是签名字符这一事实,而 printf 将它们视为..好吧..签名字符/整数。他的所有字节都高于> 0x80,因此它们本质上是有符号的。另外 ((unsigned char)new_buf) 根本不会帮助他,他只会得到第一个字符,无符号。
  • @GeorgeAndré 嗯,是的。你是对的,因为 OP 可能想要打印整个内容(至少我从一条评论中读到了这一点),这应该只是演示如何访问 new_buf 的元素,因此结果将是有意义的,纠正上面的错误.打印第一个字节是我的意图,所以它确实有帮助。您关于字节值的观点是正确的,但我完全错过了它。我会编辑它。
【解决方案3】:

如果您尝试复制一个整数并将其作为整数打印到标准输出,以 16 为基数:

char new_buf[4];
...
printf("%x\n", *new_buf);

无论您在new_buf 中存储什么,它的类型 仍然是char[4]。所以,*new_buf类型char(它与new_buf[0] 相同)。

因此,您将获取整数的第一个 char(可能是高字节或低字节,具体取决于平台),将其自动提升为整数,然后将其打印为以 16 为基数的无符号整数。

memcpy 确实已将您的值复制到数组中,但如果您想打印它,请使用

printf("%x\n", *(uint32_t *)new_buf);

printf("%02x%02x%02x%02x\n", new_buf[0], new_buf[1], new_buf[2], new_buf[3]);

(但请注意,在后一种情况下,您的字节顺序可能会颠倒,具体取决于平台)。


如果您尝试创建一个包含数字的 base-16 字符串表示形式的 char 数组:

不要使用memcpy,它不会从整数转换为其字符串表示形式。

试试

uint32_t after = 0xe1ca95ee;
char new_buf[1 + 2*sizeof(after)];
snprintf(new_buf, sizeof(new_buf), "%x", after);
printf("original %x formatted as '%s'\n", after, new_buf);

(缓冲区的大小为每个八位字节提供 2 个字符,加上一个用于 nul 终止符)。

【讨论】:

  • *(uint32_t *)new_buf 触发未定义的行为。指针别名规则是不对称的:您可以通过具有字符类型的指针访问任何类型的对象,但您可以通过具有非字符类型的指针访问声明为具有字符类型的对象.
  • @zwol,可以通过指向union { char c[sizeof(uint32_t)]; uint32_t i32; } 的指针从char 缓冲区读取和写入32 位整数吗?
  • @Ant_222 不是通过指向uint32_t的指针;只能通过字面访问该联合的i32 字段。而且,令人愤怒的是,C++ 中根本没有(C99 勘误中对联合规则的关键调整从未被 C++ 标准采纳)。
猜你喜欢
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多