print hex value of char

K

Keep Asking

I totally mess up my last post. Let me start it again.

I want print hex value from char, on 64 platform.
uname -a
Linux myang-desktop 2.6.24-26-generic #1 SMP Tue Dec 1 17:55:03 UTC
2009 x86_64 GNU/Linux

gcc --version
gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

Hese is the program


#include <stdio.h>
#include <stdio.h>


int main()
{
char array[] = {0x7f,0x55,0x86,0x91};
int i=0;

for(i=0; i<4; i++)
{
printf("%02x\n", (unsigned char)array);
printf("%02x\n", (unsigned int)array);
printf("%02x\n", array);
}

}


Here is the output

../a.out
7f
7f
7f
55
55
55
86
ffffff86
ffffff86
91
ffffff91
ffffff91


Only line
printf("%02x\n", (unsigned char)array);
gives me what I want. Can anyone explain?

Thanks,
 
J

Joel J. Adamson

Keep Asking said:
I want print hex value from char, on 64 platform.
printf("%02x\n", (unsigned char)array);


unsigned char ranges from 0 (0x0) to 255 (0xff = 2^8 - 1), therefore you
get two hex digits for expressing the value.
printf("%02x\n", (unsigned int)array);


Look up the range for this type, but you get all the positions above 2^8
- 1 (0xff) filled with 1.
55
55
55
86

Note that these *are* the hex digit values, but they don't require
digits above 9.

This is the same, just with all the more significant positions filled
with 1 (giving f for each corresponding nibble).
Only line
printf("%02x\n", (unsigned char)array);
gives me what I want. Can anyone explain?


You have to define "what I want" in terms of a data type.

Joel
 
B

Ben Bacarisse

Keep Asking said:
I totally mess up my last post. Let me start it again.

I want print hex value from char, on 64 platform.
uname -a
Linux myang-desktop 2.6.24-26-generic #1 SMP Tue Dec 1 17:55:03 UTC
2009 x86_64 GNU/Linux

gcc --version
gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

Hese is the program


#include <stdio.h>
#include <stdio.h>

One will do!
int main()
{
char array[] = {0x7f,0x55,0x86,0x91};

int i=0;

for(i=0; i<4; i++)
{
printf("%02x\n", (unsigned char)array);
printf("%02x\n", (unsigned int)array);
printf("%02x\n", array);
}
}


Here is the output

./a.out
7f
7f
7f
55
55
55
86
ffffff86
ffffff86
91
ffffff91
ffffff91


Only line
printf("%02x\n", (unsigned char)array);
gives me what I want. Can anyone explain?


The key things to know are:

(a) char may be (and on your system is) signed. Thus some char values
are negative integers. The char you want to print as "86" has a
mathematical value of -122. To see why, you need to know how signed
numbers are represented (see [1]).

(b) Converting from a signed integer to an unsigned type is defined by
adding (repeatedly if necessary) one more than the largest unsigned
number the unsigned type can hold. On your machine, unsigned int can
hold numbers up to 4294967295 which is 2**32-1. One more than this is
2**32 or 4294967296, so the result of converting -122 will be
4294967296-122 = 4294967174. This, in hex, is ffffff86. It is no
accident that the last two digits are 86 -- but it helps to know what C
is doing.

(c) %02x mean use a field width of at least 2. That is why numbers that
need more get more.

You can do this another way: printf("%hhx\n", c); but older C does not
have this length modifier so your first method is the most portable.
Also, of you declared array to have type unsigned char, the cast would
not be needed.

Note that you are deliberately asking for the "wrong" answer -- the
hexadecimal for -122 is -7a!

[1] http://en.wikipedia.org/wiki/Signed_number_representations
 
K

Keep Asking

Keep Asking said:
I totally mess up my last post. Let me start it again.
I want print hex value from char, on 64 platform.
uname -a
Linux myang-desktop 2.6.24-26-generic #1 SMP Tue Dec 1 17:55:03 UTC
2009 x86_64 GNU/Linux
gcc --version
gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There
is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
Hese is the program
#include <stdio.h>
#include <stdio.h>

One will do!


int main()
{
  char array[] = {0x7f,0x55,0x86,0x91};
  int i=0;
  for(i=0; i<4; i++)
    {
      printf("%02x\n", (unsigned char)array);
      printf("%02x\n", (unsigned int)array);
      printf("%02x\n", array);
    }
}

Here is the output
./a.out
7f
7f
7f
55
55
55
86
ffffff86
ffffff86
91
ffffff91
ffffff91

Only line
      printf("%02x\n", (unsigned char)array);
gives me what I want. Can anyone explain?


The key things to know are:

(a) char may be (and on your system is) signed.  Thus some char values
are negative integers.  The char you want to print as "86" has a
mathematical value of -122.  To see why, you need to know how signed
numbers are represented (see [1]).

(b) Converting from a signed integer to an unsigned type is defined by
adding (repeatedly if necessary) one more than the largest unsigned
number the unsigned type can hold.  On your machine, unsigned int can
hold numbers up to 4294967295 which is 2**32-1.  One more than this is
2**32 or 4294967296, so the result of converting -122 will be
4294967296-122 = 4294967174.  This, in hex, is ffffff86.  It is no
accident that the last two digits are 86 -- but it helps to know what C
is doing.

(c) %02x mean use a field width of at least 2.  That is why numbers that
need more get more.

You can do this another way: printf("%hhx\n", c); but older C does not
have this length modifier so your first method is the most portable.
Also, of you declared array to have type unsigned char, the cast would
not be needed.

Note that you are deliberately asking for the "wrong" answer -- the
hexadecimal for -122 is -7a!

[1]http://en.wikipedia.org/wiki/Signed_number_representations

I can see why printf("%02x\n", (unsigned int)array);
does not work.

But I do not understand why printf("%02x\n", array);
did not work.
 
K

Keith Thompson

Kenneth Brody said:
On 7/7/2010 1:59 PM, Keep Asking wrote: [...]
But I do not understand why printf("%02x\n", array);
did not work.


Because array is of type "char", which on your system is signed. When
(signed) char is promoted to (signed) int for the varadic function printf(),
the sign gets extended with it. (That is, the char -122 gets promoted to
the int -122, which on your system is represented by the 32-bit value
"0xffffff86".


More precisely, the int value -122 is passed to printf, which then,
because of the "%02x" format, interprets it as a value of type
unsigned int. The resulting unsigned int value can vary depending
on which representation your system uses for signed integers.
Almost all systems use 2's-complement, so "ffffff86" is the most
likely result (though it could be "ff86" if int is 16 bits, or
"ffffffffffffff86" if int is 64 bits, or ...).
 
B

Ben Bacarisse

Keep Asking said:
int main()
{
  char array[] = {0x7f,0x55,0x86,0x91};
  int i=0;
  for(i=0; i<4; i++)
    {
      printf("%02x\n", (unsigned char)array);
      printf("%02x\n", (unsigned int)array);
      printf("%02x\n", array);
    }
}

I can see why printf("%02x\n", (unsigned int)array);
does not work.

But I do not understand why printf("%02x\n", array);
did not work.


The %x format is for printing unsigned int. The argument is converted to
unsigned int and printed in hex. In effect, somewhere deep inside the
implementation of printf there is a cast to unsigned int just like the
explicit one in the case you understand.

Yes, there is a technicality that the argument gets promoted from a char
value of -122 to an int value of -122 but that has no substantive
effect.
 
K

Keith Thompson

Ben Bacarisse said:
Keep Asking said:
int main()
{
  char array[] = {0x7f,0x55,0x86,0x91};

  int i=0;

  for(i=0; i<4; i++)
    {
      printf("%02x\n", (unsigned char)array);
      printf("%02x\n", (unsigned int)array);
      printf("%02x\n", array);
    }
}

I can see why printf("%02x\n", (unsigned int)array);
does not work.

But I do not understand why printf("%02x\n", array);
did not work.


The %x format is for printing unsigned int. The argument is converted to
unsigned int and printed in hex. In effect, somewhere deep inside the
implementation of printf there is a cast to unsigned int just like the
explicit one in the case you understand.

Yes, there is a technicality that the argument gets promoted from a char
value of -122 to an int value of -122 but that has no substantive
effect.


No, the argument isn't *converted*, at least not in the usual sense of
the word. The argument is of type int; it's *interpreted* as if it were
of type unsigned int. (If there were a conversion, how would the
implementation of printf know what type to convert from?)

For values that are representable in either type (in the range
0 .. INT_MAX), there's a special-case guarantee in C99 6.2.5p9:

The range of nonnegative values of a signed integer type is a
subrange of the corresponding unsigned integer type, and the
representation of the same value in each type is the same.

with a footnote:

The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values
from functions, and members of unions.

But for negative values, there's no such guarantee, and I'd expect
the printed value to vary depending on whether the implementation
uses 2's-complement, 1s'-complement, or sign-and-magnitude.
 
B

Ben Bacarisse

Keith Thompson said:
Ben Bacarisse said:
Keep Asking said:
int main()
{
  char array[] = {0x7f,0x55,0x86,0x91};

  int i=0;

  for(i=0; i<4; i++)
    {
      printf("%02x\n", (unsigned char)array);
      printf("%02x\n", (unsigned int)array);
      printf("%02x\n", array);
    }
}

I can see why printf("%02x\n", (unsigned int)array);
does not work.

But I do not understand why printf("%02x\n", array);
did not work.


The %x format is for printing unsigned int. The argument is converted to
unsigned int and printed in hex. In effect, somewhere deep inside the
implementation of printf there is a cast to unsigned int just like the
explicit one in the case you understand.

Yes, there is a technicality that the argument gets promoted from a char
value of -122 to an int value of -122 but that has no substantive
effect.


No, the argument isn't *converted*, at least not in the usual sense of
the word. The argument is of type int; it's *interpreted* as if it were
of type unsigned int. (If there were a conversion, how would the
implementation of printf know what type to convert from?)


You are right. I was extending what happens when one of the "short"
length modifiers is used (hx and hhx) to the general case. hx and hhx
do cause an actual conversion, but then the actual promoted type to
convert from is know to the implementation.
For values that are representable in either type (in the range
0 .. INT_MAX), there's a special-case guarantee in C99 6.2.5p9:

The range of nonnegative values of a signed integer type is a
subrange of the corresponding unsigned integer type, and the
representation of the same value in each type is the same.

with a footnote:

The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values
from functions, and members of unions.

But for negative values, there's no such guarantee, and I'd expect
the printed value to vary depending on whether the implementation
uses 2's-complement, 1s'-complement, or sign-and-magnitude.

I am not so sure. In retrospect, I'd say that 7.19.6.1 p9:

... If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.

suggests that it is undefined behaviour. I don't think the special
dispensation of 6.2.5 p9 (or that of 6.5.2.2 p6) can alter that but I
only say "suggests" because there may be wiggle room in the meaning of
"the correct type".
 
K

Keith Thompson

Ben Bacarisse said:
Keith Thompson <[email protected]> writes:
[...]

[Context: passing a negative int value to printf with a "%02x" format]
I am not so sure. In retrospect, I'd say that 7.19.6.1 p9:

... If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.

suggests that it is undefined behaviour. I don't think the special
dispensation of 6.2.5 p9 (or that of 6.5.2.2 p6) can alter that but I
only say "suggests" because there may be wiggle room in the meaning of
"the correct type".

By "I'd expect", I meant that most, perhaps all, implementations
behave that way, not that the behavior is specified by the standard.

The standard defines the behavior only for values that are
representable both in a signed type and in the corresponding unsigned
type, implying that the behavior is undefined in other cases.
But most implementations would have to go out of their way to have
anything other than the obvious behavior.

Then again, I can imagine an optimizing compiler *assuming* that
the argument is non-negative, and thereby generating code that
behaves badly when the assumption is violated.
 
F

FredK

Ben Bacarisse said:
Keep Asking <[email protected]> writes:
(c) %02x mean use a field width of at least 2. That is why numbers that
need more get more.

You can do this another way: printf("%hhx\n", c); but older C does not
have this length modifier so your first method is the most portable.
Also, of you declared array to have type unsigned char, the cast would
not be needed.

%2.2x should also work
 
B

Ben Bacarisse

Keith Thompson said:
Ben Bacarisse said:
Keith Thompson <[email protected]> writes:
[...]

[Context: passing a negative int value to printf with a "%02x" format]
I am not so sure. In retrospect, I'd say that 7.19.6.1 p9:

... If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.

suggests that it is undefined behaviour. I don't think the special
dispensation of 6.2.5 p9 (or that of 6.5.2.2 p6) can alter that but I
only say "suggests" because there may be wiggle room in the meaning of
"the correct type".

By "I'd expect", I meant that most, perhaps all, implementations
behave that way, not that the behavior is specified by the standard.

The standard defines the behavior only for values that are
representable both in a signed type and in the corresponding unsigned
type, implying that the behavior is undefined in other cases.

Would you say that an argument of type int is of the correct type for
a %x conversion when its value is non-negative? That seems like an
unnatural interpretation of what the phrase "correct type" might mean.
But most implementations would have to go out of their way to have
anything other than the obvious behavior.

I agree about what is likely to happen.
 
K

Keith Thompson

Ben Bacarisse said:
Would you say that an argument of type int is of the correct type for
a %x conversion when its value is non-negative? That seems like an
unnatural interpretation of what the phrase "correct type" might mean.

It's certainly an unnatural interpretation of the phrase "*the*
correct type".

I think that the intent was for the footnote on 6.2.5p9:

The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values
from functions, and members of unions.

to apply to arguments to printf, but if so the standard doesn't
state it very clearly. I suspect that the author of 7.19.6.1p9:

If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.

just wasn't thinking about corresponding signed and unsigned types.
 
F

FredK

Ben Bacarisse said:
No. The .2 gives the minimum number of digits that must appear when
used with a d, i, o, u, x or X conversion.

Odd %2.2x has always worked for me when extracting a byte in a printf
statement picking apart an int. I'll have to give it a test again.
 
B

Ben Bacarisse

FredK said:
Odd %2.2x has always worked for me when extracting a byte in a printf
statement picking apart an int. I'll have to give it a test again.

#include <stdio.h>

int main(void)
{
printf("%2.2x\n", -1);
return 0;
}

output from my gcc/glibc combination:

ffffffff

as expected from reading the standard (both C89 and C99). If you get
"ff" as the output, then I'd say you need to file a bug against your
system's printf implementation!
 
T

Tim Rentsch

Ben Bacarisse said:
Keith Thompson said:
Ben Bacarisse said:
<snip all but the code>
int main()
{
char array[] = {0x7f,0x55,0x86,0x91};

int i=0;

for(i=0; i<4; i++)
{
printf("%02x\n", (unsigned char)array);
printf("%02x\n", (unsigned int)array);
printf("%02x\n", array);
}
}
<snip>
I can see why printf("%02x\n", (unsigned int)array);
does not work.

But I do not understand why printf("%02x\n", array);
did not work.

The %x format is for printing unsigned int. The argument is converted to
unsigned int and printed in hex. In effect, somewhere deep inside the
implementation of printf there is a cast to unsigned int just like the
explicit one in the case you understand.

Yes, there is a technicality that the argument gets promoted from a char
value of -122 to an int value of -122 but that has no substantive
effect.


No, the argument isn't *converted*, at least not in the usual sense of
the word. The argument is of type int; it's *interpreted* as if it were
of type unsigned int. (If there were a conversion, how would the
implementation of printf know what type to convert from?)


You are right. I was extending what happens when one of the "short"
length modifiers is used (hx and hhx) to the general case. hx and hhx
do cause an actual conversion, but then the actual promoted type to
convert from is know to the implementation.
For values that are representable in either type (in the range
0 .. INT_MAX), there's a special-case guarantee in C99 6.2.5p9:

The range of nonnegative values of a signed integer type is a
subrange of the corresponding unsigned integer type, and the
representation of the same value in each type is the same.

with a footnote:

The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values
from functions, and members of unions.

But for negative values, there's no such guarantee, and I'd expect
the printed value to vary depending on whether the implementation
uses 2's-complement, 1s'-complement, or sign-and-magnitude.

I am not so sure. In retrospect, I'd say that 7.19.6.1 p9:

... If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.

suggests that it is undefined behaviour. I don't think the special
dispensation of 6.2.5 p9 (or that of 6.5.2.2 p6) can alter that but I
only say "suggests" because there may be wiggle room in the meaning of
"the correct type".


This question has come up before. Some are of the opinion that
what the phrase "the correct type" means is in fact defined by
7.15.1.1 p2 (the same conditions as are in 6.5.2.2 p6); others
that the meaning of "the correct type" is independent of the
conditions of 7.15.1.1 p2 or 6.5.2.2 p6 (although of course the
call is presumably constrained by those conditions). My own view
is that the committee expected "the correct type" to be read in
the sense of the conditions of 7.15.1.1 p2. In the context of
comp.lang.c, I don't see any reason to suppose anything different,
since no conforming implementation is ever going to be weaker than
that. In comp.std.c the point may be worth debating, and it's
probably worth mentioning here, but as a practical matter the
conditions of 7.15.1.1 p2 may reasonably be assumed (IMO of course)
to be reliably completely portable.
 
T

Tim Rentsch

Keith Thompson said:
Ben Bacarisse said:
Keith Thompson <[email protected]> writes:
[...]

[Context: passing a negative int value to printf with a "%02x" format]
I am not so sure. In retrospect, I'd say that 7.19.6.1 p9:

... If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.

suggests that it is undefined behaviour. I don't think the special
dispensation of 6.2.5 p9 (or that of 6.5.2.2 p6) can alter that but I
only say "suggests" because there may be wiggle room in the meaning of
"the correct type".

By "I'd expect", I meant that most, perhaps all, implementations
behave that way, not that the behavior is specified by the standard.

The standard defines the behavior only for values that are
representable both in a signed type and in the corresponding unsigned
type, implying that the behavior is undefined in other cases.
But most implementations would have to go out of their way to have
anything other than the obvious behavior.

Then again, I can imagine an optimizing compiler *assuming* that
the argument is non-negative, and thereby generating code that
behaves badly when the assumption is violated.

Perhaps more to the point, a negative signed value may be
a trap representation when interpreted as an unsigned value
(or the other way around of course). In fact such circumstances
may occur even in "reasonable" implementations, if an unsigned
value corresponding to a negative-zero-trap-representation is
interpreted as a signed value. Even implementations that use
two's complement are allowed a trap representation for the
distinguished value.
 
T

Tim Rentsch

Ben Bacarisse said:
Keith Thompson said:
Ben Bacarisse said:
Keith Thompson <[email protected]> writes:
[...]

[Context: passing a negative int value to printf with a "%02x" format]
But for negative values, there's no such guarantee, and I'd expect
the printed value to vary depending on whether the implementation
uses 2's-complement, 1s'-complement, or sign-and-magnitude.

I am not so sure. In retrospect, I'd say that 7.19.6.1 p9:

... If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.

suggests that it is undefined behaviour. I don't think the special
dispensation of 6.2.5 p9 (or that of 6.5.2.2 p6) can alter that but I
only say "suggests" because there may be wiggle room in the meaning of
"the correct type".

By "I'd expect", I meant that most, perhaps all, implementations
behave that way, not that the behavior is specified by the standard.

The standard defines the behavior only for values that are
representable both in a signed type and in the corresponding unsigned
type, implying that the behavior is undefined in other cases.

Would you say that an argument of type int is of the correct type for
a %x conversion when its value is non-negative? That seems like an
unnatural interpretation of what the phrase "correct type" might mean.

I would say it is the most natural interpretation, in light
of 7.15.1.1 p2. In every other case I'm aware of, the
Standard is very careful to define exactly which types
satisfy the requirements of the contexts where they are
used; it would be very surprising if a phrase like "the
correct type" were left up to interpretation. If something
other than the conditions of 7.15.1.1 p2 were meant to apply
to this phrase, I expect the Standard would explicitly spell
out either precisely what the rules are or exactly which
other section(s) of the Standard define the requirements.
Since it doesn't, 7.15.1.1 p2 seems like the most natural
candidate to provide a definitional context for the phrase.
 
D

Denis McMahon

I totally mess up my last post. Let me start it again.
I want print hex value from char, on 64 platform.
char array[] = {0x7f,0x55,0x86,0x91}; // implicitly signed?
printf("%02x\n", (unsigned char)array); // first case
printf("%02x\n", (unsigned int)array); // second case
printf("%02x\n", array); // third case


%X expects an unsigned int.

So in the first case, you are casting a signed char to unsigned char,
and then implicitly casting to unsigned int

in the second case, you are explicitly casting a signed char to unsigned int

in the third case, you are implicitly casting a signed char to unsigned int
7f
7f
7f
55
55
55
86
ffffff86
ffffff86
91
ffffff91
ffffff91
Only line
printf("%02x\n", (unsigned char)array);
gives me what I want. Can anyone explain?


Perhaps casting from unsigned char to unsigned int (case 1) always fills
bits 8..31 with 0, whereas casting from signed char to unsigned int
fills them with the sign bit.

A better format specifier for a signed char as hex might be %02hhX (ISO C99)

printf("%02hhx\n", array); // print character value in hex

Rgds

Denis McMahon
 
K

Keith Thompson

Tim Rentsch said:
I would say it is the most natural interpretation, in light
of 7.15.1.1 p2. In every other case I'm aware of, the
Standard is very careful to define exactly which types
satisfy the requirements of the contexts where they are
used; it would be very surprising if a phrase like "the
correct type" were left up to interpretation. If something
other than the conditions of 7.15.1.1 p2 were meant to apply
to this phrase, I expect the Standard would explicitly spell
out either precisely what the rules are or exactly which
other section(s) of the Standard define the requirements.
Since it doesn't, 7.15.1.1 p2 seems like the most natural
candidate to provide a definitional context for the phrase.

I agree that that was probably the intent. The real problem is that
7.19.6.1p9 uses the phrase "*the* correct type", strongly implying
that there's only one such type.

I think this is an error in the standard, which should be corrected
by replacing the phrase "the correct type" with a reference to
7.15.1.1p2.

As it is, the standard doesn't actually say that the printf()
and friends use <stdarg.h> to process their arguments, so it's not
clear that anything in 7.15 is applicable. (Except that printf
can be called via a pointer, so a given call could randomly be
either to printf or to some function that does use <stdarg.h>.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top