2
  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

I want to copy the content of after to new_buf. But the result is confusing. printf gives me ffffffee. It looks like an address. I have already dereferenced new_buf.

According to the comments, I can't use memcpy or strncpy to do this task. But why? memcpy and strncpy are only designed to handle char *? But the content of after is in memory.

PS: I know I should use sprintf or snprintf. If you can explain why memcpy and strncpy is not for this case, I appreciate it.

dda
  • 6,030
  • 2
  • 25
  • 34
JACK M
  • 2,627
  • 3
  • 25
  • 43
  • 2
    are you sure you want to do `memcpy()`? – Sourav Ghosh Aug 03 '15 at 14:40
  • why not http://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm – tudoricc Aug 03 '15 at 14:41
  • 1
    What exactly did you expect to be printed? There are several different things you could have been trying to accomplish and I can't tell which. – zwol Aug 03 '15 at 14:47
  • 1
    Until the larger issues of why code needs to "copying integer to char buffer", the answers will still be coming up short. Copy `uint32_t after` to a `uint8_t new_buf8[4];` makes some sense, but to a `char []` hints to the beginning of a larger coding goal that is problematic. – chux - Reinstate Monica Aug 03 '15 at 15:13

3 Answers3

7

This is the problem right here:

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

This gives you what you asked for: it prints the char at location new_buf using %x format. that location contains 0xee already (after your successful memcpy, least significant byte first, since you're most probably on an Intel machine, little endian), but it is printed as 0xffffffee (a negative number), since it's a char and not an unsigned char, and because ofcourse 0xee is a signed byte (> 0x7F, highest bit set)

You should use instead:

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

...edited below...

Or rather:

printf("%x\n", *((uint32_t*)new_buf));
George André
  • 770
  • 1
  • 6
  • 14
  • Also, be aware of endianess. – George André Aug 03 '15 at 14:55
  • 2
    Both of your suggested constructs have undefined behavior due to the asymmetry of the pointer-aliasing rules. (You can access an object of any type via a pointer with character type, but you can **not** access an object declared to have character type via a pointer with non-character type.) – zwol Aug 03 '15 at 15:49
  • @zwol but we're not accessing an object of character type. We're accessing an object of `char[4]` type. – Petr Skocik Aug 03 '15 at 15:54
  • @PSkocik "Character type" is a term-of-art in the C standard which encompasses both single `char` objects and arrays of them (and probably some other stuff I don't remember off the top of my head). Regardless, the point is that `char[4]` and `int` are not compatible types and therefore you cannot use `int*` to access memory declared as `char[4]`, *even though* you *can* use `char*` to access memory declared as `int`. There is a special exception for `char*` but it only goes in one direction. – zwol Aug 03 '15 at 16:00
  • @zwol Of course it's undefined. It's undefined because it depends on the bit representation of ints (i.e., big endian, little endian,...). But do you have a pointer of type http which explains why it should be undefined any more than that? – Petr Skocik Aug 03 '15 at 16:09
  • @zwol Any way, if you're talking about https://en.wikipedia.org/wiki/Pointer_aliasing, at no point are we storing fundamentally different pointers to the same chunk of data so I don't see how that would apply here. – Petr Skocik Aug 03 '15 at 16:23
  • @PSkocik Dependence on the bit representation of an integer is usually only enough to make something unspecified or implementation-defined. (For instance, given `union { int x; unsigned char c[sizeof(int)]; } u; u.x = 0x12345678;` the values of `u.c[0]` through `u.c[sizeof(int)]` are *unspecified.*) (cont'd...) – zwol Aug 03 '15 at 17:05
  • @PSkocik http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf is the closest approximation to the text of ISO C2011 that is publicly available online for free. The relevant text is on physical page 95 of that document (section 6.5, paragraphs 6 and 7). You don't need *two different pointers* to the same chunk of data to make something undefined by type-based aliasing violation; you only need a chunk of data with a declared type, plus a pointer to that address with an incompatible type. – zwol Aug 03 '15 at 17:08
  • @PSkocik See https://stackoverflow.com/questions/30967447/ub-on-reading-object-using-non-character-type-when-last-written-using-character for a great deal more discussion. – zwol Aug 03 '15 at 17:09
  • @zwol OK, I see how that makes sense. Thanks for that info. – Petr Skocik Aug 03 '15 at 17:51
3

If you do this:

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

You can see it prints

ffffffee
ffffff95
ffffffca
ffffffe1

So your bytes are all there. As pointed out by @George André, they are signed bytes, so you see fs being padded to the front because the numbers are negative and it always prints 4 bytes, ee represented in 32 bits is ffffffee. You are probably on a little-endian machine so that the least significant byte ee is actually stored at the lowest memory position, which is why you get the "last" byte of your number when dereferencing new_buf. The other part is answered already by others, you must declare new_buf as unsigned or cast during printing.

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

Or alternatively

   uint32_t after = 0xe1ca95ee;
   unsinged char new_buf[4];
   memcpy(new_buf, &after, 4);
   printf("%x\n", *new_buf);
oarfish
  • 4,116
  • 4
  • 37
  • 66
  • This answer is wrong, he wants to print out the uint32_t at new_buf location. Also the "ones" come from the fact that new_buf is signed char, and printf treats those as.. well.. signed chars/integers. all of his bytes are higher than >0x80, so they are inherently signed. Also *((unsigned char*)new_buf) won't help him at all, he will get the first character, unsigned, only. – George André Aug 03 '15 at 15:10
  • @GeorgeAndré Well yes. You are right insofar as the OP probably wants to print the whole thing (at least I read that from the one comment), this should simply demonstrate how to access the elements of `new_buf` so the results will make sense, correcting the errors above. Printing the first byte was my intention, so it *does* help. Your point about the byte values is right though, I totally missed it. I'll edit that out. – oarfish Aug 03 '15 at 20:37
0

If you're trying to copy an integer and print it to stdout, as an integer, in base 16:

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

Whatever you stored in new_buf, it's type is still char[4]. So, the type of *new_buf is char (it's identical to new_buf[0]).

So, you're getting the first char of your integer (which may be the high or low byte, depending on platform), having it automatically promoted to an integer, and then printing that as an unsigned int in base 16.

memcpy has indeed copied your value into the array, but if you want to print it, use

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

or

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

(but note in the latter case your byte order may be reversed, depending on platform).


If you're trying to create a char array containing a base-16 string representation of your number:

Don't use memcpy, that doesn't convert from the integer to its string representation.

Try

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

(the buffer is sized to give 2 chars per octet, plus one for the nul-terminator).

Useless
  • 64,155
  • 6
  • 88
  • 132
  • `*(uint32_t *)new_buf` triggers undefined behavior. The pointer-aliasing rules are asymmetric: you can access an object of any type via a pointer with character type, but you can **not** access an object declared to have character type via a pointer with non-character type. – zwol Aug 03 '15 at 15:50
  • @zwol, can one read and write 32-bit integers from and to a `char` buffer through a pointer to `union { char c[sizeof(uint32_t)]; uint32_t i32; }`? – Anton Shepelev Nov 14 '18 at 11:04
  • 1
    @Ant_222 Not through a pointer to `uint32_t`; only by literally accessing the `i32` field of that union. And, infuriatingly, not in C++ at all (a crucial adjustment to the rules for unions in a corrigiendum for C99 has never been picked up by the C++ standard). – zwol Nov 14 '18 at 12:12