14

I want to print a character string in hexadecimal format on machine A. Something like:

ori_mesg = gen_rdm_bytestream (1400, seed)
sendto(machine B, ori_mesg, len(mesg))

On machine B

recvfrom(machine A, mesg)

mesg_check = gen_rdm_bytestream (1400, seed)

for(i=0;i<20;i++){
    printf("%02x ", *(mesg+i)& 0xFF);
}
printf("\n");
for(i=0; i<20; i++){
    printf("%02x ", *(mesg_check+i));
}
printf("\n");

seed varies among 1, 2, 3, ...

The bytes generation funcion is:

u_char *gen_rdm_bytestream (size_t num_bytes, unsigned int seed)
{
    u_char *stream = malloc (num_bytes+4);
    size_t i;

    u_int16_t seq = seed;
    seq = htons(seq);
    u_int16_t tail = num_bytes;
    tail = htons(tail);
    memcpy(stream, &seq, sizeof(seq));
    srand(seed);
    for (i = 3; i < num_bytes+2; i++){
        stream[i] = rand ();
    }
    memcpy(stream+num_bytes+2, &tail, sizeof(tail));

    return stream;
}

But I got results from printf like:

00 01 00 67 c6 69 73 51 ff 4a ec 29 cd ba ab f2 fb e3 46 7c
00 01 00 67 ffffffc6 69 73 51 ffffffff 4a ffffffec 29 ffffffcd ffffffba ffffffab fffffff2 fffffffb ffffffe3 46 7c

or

00 02 88 fa 7f 44 4f d5 d2 00 2d 29 4b 96 c3 4d c5 7d 29 7e
00 02 00 fffffffa 7f 44 4f ffffffd5 ffffffd2 00 2d 29 4b ffffff96 ffffffc3 4d ffffffc5 7d 29 7e

Why are there so many fffff for mesg_check?

Are there any potential reasons for this phenomenon?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
misteryes
  • 2,167
  • 4
  • 32
  • 58
  • 2
    What do `mesg` and `mesg_check` look like? – squiguy Apr 04 '13 at 23:41
  • 1
    would mesg be a char* or char[] by any chance? – fvu Apr 04 '13 at 23:42
  • what is `mesg` and `mesg_check`? Maybe pointer arithmetic problems? – Neigyl R. Noval Apr 04 '13 at 23:42
  • 1
    Try to put a `space` in the printf specifier. Ie, "%2.2x " You are getting many number put together and negatives are being sign extended. – artless noise Apr 04 '13 at 23:56
  • `for (i = 3; i < num_bytes+2; i++){` <- you're leaving `stream[2]` unwritten-to. – Daniel Fischer Apr 05 '13 at 00:01
  • `%2.2x` ??? what does this mean? – misteryes Apr 05 '13 at 00:08
  • @misteryes `%2.2x` means to make the max/min of the hex string 2 characters. So it will only ever print two digits. You seem to know it is a single byte, so no matter how the compiler converts things, you will end up with only two ASCII hex characters. After your edit, it still looks like a conversion issue. You can also use `*(mesg_check+i) & 0xff`, if that is the way you want to go. – artless noise Apr 05 '13 at 00:45
  • 1
    You accepted an answer, but you still haven't shown us how `mesg` and `mesg_check` are defined -- not how you initialize them, but what their declarations look like. Showing us that could be helpful to future readers of this question. – Keith Thompson Apr 05 '13 at 14:48
  • 1
    Possible duplicate of [Why does printf not print out just one byte when printing hex?](https://stackoverflow.com/questions/3555791/why-does-printf-not-print-out-just-one-byte-when-printing-hex) – phuclv Oct 11 '17 at 06:51

2 Answers2

25

Here's a small program that illustrates the problem I think you might be having:

#include <stdio.h>
int main(void) {
    char arr[] = { 0, 16, 127, 128, 255 };
    for (int i = 0; i < sizeof arr; i ++) {
        printf(" %2x", arr[i]);
    }
    putchar('\n');
    return 0;
}

On my system (on which plain char is signed), I get this output:

  0 10 7f ffffff80 ffffffff

The value 255, when stored in a (signed) char, is stored as -1. In the printf call, it's promoted to (signed) int -- but the "%2x" format tells printf to treat it as an unsigned int, so it displays fffffffff.

Make sure that your mesg and mesg_check arrays are defined as arrays of unsigned char, not plain char.

UPDATE: Rereading this answer more than a year later, I realize it's not quite correct. Here's a program that works correctly on my system, and will almost certainly work on any reasonable system:

#include <stdio.h>
int main(void) {
    unsigned char arr[] = { 0, 16, 127, 128, 255 };
    for (int i = 0; i < sizeof arr; i ++) {
        printf(" %02x", arr[i]);
    }
    putchar('\n');
    return 0;
}

The output is:

 00 10 7f 80 ff

An argument of type unsigned char is promoted to (signed) int (assuming that int can hold all values of type unsigned char, i.e., INT_MAX >= UCHAR_MAX, which is the case on practically all systems). So the argument arr[i] is promoted to int, while the " %02x" format requires an argument of type unsigned int.

The C standard strongly implies, but doesn't quite state directly, that arguments of corresponding signed and unsigned types are interchangeable as long as they're within the range of both types -- which is the case here.

To be completely correct, you need to ensure that the argument is actually of type unsigned int:

printf("%02x", (unsigned)arr[i]);
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Can't he just use a *width* and *precision* like "%2.2x" and then it doesn't matter if it is signed or not? I guess it is more efficient to change the *sign*. – artless noise Apr 05 '13 at 00:02
  • ah, `mesg` is from `recvfrom()`, which is of type `void*`, so how can I change the bytes to unsigned char? – misteryes Apr 05 '13 at 00:12
  • 1
    +1 thats indeed what's happening, but the easiest way out is to just mask off all the rubbish, like `printf(" %2x", arr[i] & 0xFF);` – fvu Apr 05 '13 at 00:13
  • I tried something like `printf("%2x", *((u_char*)(mesg+i)));`it is still wrong, here you `arr[i]` is `mesg` or `mesg_check` ? – misteryes Apr 05 '13 at 00:18
  • I tried `for(i=0;i<20;i++){printf("%2.2x", *(mesg+i)& 0xFF); , still the results are like: `00010067c6697351ff4aec29cdbaabf2fbe3467c` and `00010067ffffffc6697351ffffffff4affffffec29ffffffcdffffffbaffffffabfffffff2fffffffbffffffe3467c` – misteryes Apr 05 '13 at 00:24
  • 1
    @misteryes: The real question is, how are `mesg` and `mesg_check` defined? Presumably you defined them yourself (in code you haven't yet shown us). If you're dealing with raw byte data, `unsigned char` is a better choice than plain `char` (for this and other reasons); plain `char` is for *character* data. You can pass an `unsigned char*` as the 2nd argument to `recvfrom()`. And if you're dumping data in hex, then `"%02x"` typically makes more sense; it prints values less than `0x10` with a leading 0. – Keith Thompson Apr 05 '13 at 00:25
  • @misteryes: At least for testing, please add spaces between the elements you're printing; otherwise it's impossible to tell where one ends and the next begins, since you're getting different lengths of output. – Keith Thompson Apr 05 '13 at 00:26
  • @misteryes: You still haven't shown us how you defined `mesg` and `mesg_check`. Are they arrays of `char`? If so, what happens when you change them to arrays of `unsigned char`? – Keith Thompson Apr 05 '13 at 00:50
-1

Yes, always print the string in hexadecimal format as:

for(i=0; till string length; i++)
    printf("%02X", (unsigned char)str[i]);

You will get an error when you try to print the whole string in one go and when printing the hexadecimal string character by character which is using 'unsigned char' if the string is in format other than 'unsigned char'.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131