C/C++将64位整数转换为字符数组

5

我有一个简单的程序,使用联合体来将64位整数与其对应的字节数组进行转换:

union u
{
  uint64_t ui;
  char c[sizeof(uint64_t)];
};

int main(int argc, char *argv[])
{
  u test;
  test.ui = 0x0123456789abcdefLL;
  for(unsigned int idx = 0; idx < sizeof(uint64_t); idx++)
    {
      cout << "test.c[" << idx << "] = 0x" << hex << +test.c[idx] << endl;
    }
  return 0;
}

我期望的输出结果是:
test.c[0] = 0xef
test.c[1] = 0xcd
test.c[2] = 0xab
test.c[3] = 0x89
test.c[4] = 0x67
test.c[5] = 0x45
test.c[6] = 0x23
test.c[7] = 0x1

但实际上我得到的是:
test.c[0] = 0xffffffef
test.c[1] = 0xffffffcd
test.c[2] = 0xffffffab
test.c[3] = 0xffffff89
test.c[4] = 0x67
test.c[5] = 0x45
test.c[6] = 0x23
test.c[7] = 0x1

我在Ubuntu LTS 14.04上使用GCC看到这个问题。我已经试图理解这个问题一段时间了。为什么char数组的前4个元素显示为32位整数,并带有0xffffff前缀?而且为什么只有前4个,不是所有的元素?有趣的是,当我使用数组写入流(这是整个事情的最初目的)时,会写入正确的值。但是逐个比较字符显然会导致问题,因为前4个字符不等于0xef,0xcd等等。

将其转换为(char*)类型,然后读取4个字节...? - SteJ
除此之外,我可以始终使用0x000000ff掩码来获取预期的值。我只是对这种行为背后的原因感兴趣。 - tickferno
2
显然你的实现使用了有符号字符。普通整数提升将会进行符号扩展。 - ewd
我认为这是因为test.c的类型是int,根据内核构建的体系结构,它将成为16位、32位或64位字...虽然有点猜测,但我需要重新学习C++!此外,根据@ewd的评论,您可能希望将其明确转换为(unsigned int)(unsigned char) - SteJ
嘿,问题解决了!谢谢! - tickferno
关于基于 sizeof 定义大小的说明:如果您使用 sizeof(<variable name>); 而不是 sizeof(<variable type>);,则如果您稍后更改源变量的类型,则更改会级联到依赖项。在这种情况下,char c[sizeof(ui)]; 的大小始终与 ui 相同。同样,在 for 循环中 sizeof(test.ui) 也是如此。 - user4581301
5个回答

3

使用char并不是正确的做法,因为它可能是signedunsigned。使用unsigned char

union u
{
  uint64_t ui;
  unsigned char c[sizeof(uint64_t)];
};

解决了。现在的问题是将数组写入流会产生错误,因为流只接受“char *”而不是“unsigned char *”,而且我并不喜欢一直使用“reinterpret_cast”... - tickferno
1
@tickferno,如果你需要关于流媒体问题的帮助,请发布另一个明确涉及该问题的问题。 - R Sahu

2
由于前置的一元操作符+char会被提升为int。由于你的chars是有符号的,任何最高位设置为1的元素都被解释为负数,并且被提升为具有相同负值的整数。有几种不同的解决方法:
  1. 去掉+... << test.c[idx] << ...。这可能会将字符打印为字符而不是数字,因此可能不是一个好的解决方案。
  2. c声明为unsigned char。这将把它提升为unsigned int
  3. 在传递之前明确地转换+test.c[idx]... << (unsigned char)(+test.c[idx]) << ...
  4. 使用二进制&将整数的上层字节设为零:... << +test.c[idx] & 0xFF << ...。这将只显示最低位字节,无论如何提升char

char在<<运算符中不会被提升,这是由于一元+运算符造成的。如果您从代码中删除一元+,这一点很容易看出来。 - Sami Kuhmonen
去掉一元+运算符会直接打印字符,但我对它们的实际十六进制值感兴趣(否则十六进制运算符没有帮助)。另外以下代码无法通过:test.c[0] == 0xef - tickferno
好的,已经相应地进行了编辑。 - Mad Physicist

1

使用无符号字符或者使用test.c[idx] & 0xff来避免当char value > 0x7f被转换成int时的符号扩展。


0

这是无符号字符与有符号字符及其转换为整数的问题


0

一元加号使char提升为int(整数提升)。由于您有符号的 chars,值将被用作这样,并且其他字节将反映出来。

并不是只有四个是 ints,它们都是。你只是看不到它,因为前导零没有显示。

要么使用unsigned char& 0xff进行提升以获得所需的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接