Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert struct to char array in C

I'm trying to convert a struct to a char array to send over the network. However, I get some weird output from the char array when I do.

#include <stdio.h>

struct x
{
   int x;
} __attribute__((packed));


int main()
{
   struct x a;
   a.x=127;
   char *b = (char *)&a;
   int i;
   for (i=0; i<4; i++)
      printf("%02x ", b[i]);
   printf("\n");
   for (i=0; i<4; i++)
      printf("%d ", b[i]);
   printf("\n");
   return 0;
}

Here is the output for various values of a.x (on an X86 using gcc):
127:
7f 00 00 00
127 0 0 0

128:
ffffff80 00 00 00
-128 0 0 0

255:
ffffffff 00 00 00
-1 0 0 0

256:
00 01 00 00
0 1 0 0

I understand the values for 127 and 256, but why do the numbers change when going to 128? Why wouldn't it just be: 80 00 00 00 128 0 0 0

Am I forgetting to do something in the conversion process or am I forgetting something about integer representation?

*Note: This is just a small test program. In a real program I have more in the struct, better variable names, and I convert to little-endian.
*Edit: formatting

like image 983
falcojr Avatar asked Jan 11 '09 20:01

falcojr


3 Answers

What you see is the sign preserving conversion from char to int. The behavior results from the fact that on your system, char is signed (Note: char is not signed on all systems). That will lead to negative values if a bit-pattern yields to a negative value for a char. Promoting such a char to an int will preserve the sign and the int will be negative too. Note that even if you don't put a (int) explicitly, the compiler will automatically promote the character to an int when passing to printf. The solution is to convert your value to unsigned char first:

for (i=0; i<4; i++)
   printf("%02x ", (unsigned char)b[i]);

Alternatively, you can use unsigned char* from the start on:

unsigned char *b = (unsigned char *)&a;

And then you don't need any cast at the time you print it with printf.

like image 108
Johannes Schaub - litb Avatar answered Oct 10 '22 23:10

Johannes Schaub - litb


The x format specifier by itself says that the argument is an int, and since the number is negative, printf requires eight characters to show all four non-zero bytes of the int-sized value. The 0 modifier tells to pad the output with zeros, and the 2 modifier says that the minimum output should be two characters long. As far as I can tell, printf doesn't provide a way to specify a maximum width, except for strings.

Now then, you're only passing a char, so bare x tells the function to use the full int that got passed instead — due to default argument promotion for "..." parameters. Try the hh modifier to tell the function to treat the argument as just a char instead:

printf("%02hhx", b[i]);
like image 31
Rob Kennedy Avatar answered Oct 10 '22 23:10

Rob Kennedy


char is a signed type; so with two's complement, 0x80 is -128 for an 8-bit integer (i.e. a byte)

like image 20
Rowland Shaw Avatar answered Oct 10 '22 23:10

Rowland Shaw