Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C/C++ Converting a 64 bit integer to char array

I have the following simple program that uses a union to convert between a 64 bit integer and its corresponding byte array:

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;
}

What I would expect as output is:

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

But what I actually get is:

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

I'm seeing this on Ubuntu LTS 14.04 with GCC.

I've been trying to get my head around this for some time now. Why are the first 4 elements of the char array displayed as 32 bit integers, with 0xffffff prepended to them? And why only the first 4, why not all of them?
Interestingly enough, when I use the array to write to a stream (which was the original purpose of the whole thing), the correct values are written. But comparing the array char by char obviously leads to problems, since the first 4 chars are not equal 0xef, 0xcd, and so on.

like image 530
tickferno Avatar asked Sep 01 '15 15:09

tickferno


2 Answers

Using char is not the right thing to do since it could be signed or unsigned. Use unsigned char.

union u
{
  uint64_t ui;
  unsigned char c[sizeof(uint64_t)];
};
like image 191
R Sahu Avatar answered Oct 04 '22 08:10

R Sahu


char gets promoted to an int because of the prepended unary + operator. . Since your chars are signed, any element with the highest by set to 1 is interpreted as a negative number and promoted to an integer with the same negative value. There are a few different ways to solve this:

  1. Drop the +: ... << test.c[idx] << .... This may print the char as a character rather than a number, so is probably not a good solution.
  2. Declare c as unsigned char. This will promote it to an unsigned int.
  3. Explicitly cast +test.c[idx] before it is passed: ... << (unsigned char)(+test.c[idx]) << ...
  4. Set the upper bytes of the integer to zero using binary &: ... << +test.c[idx] & 0xFF << .... This will only display the lowest-order byte no matter how the char is promoted.
like image 30
Mad Physicist Avatar answered Oct 04 '22 09:10

Mad Physicist