Usefulness of %hd in printf

F

Flash Gordon

Keith Thompson wrote, On 23/01/07 17:56:
Old Wolf said:
You caused undefined behaviour by not passing an argument of
type 'short int' to %hd

Interesting.

In the description of fprintf(), C99 7.19.6.1p7 says that the 'h'
modifier

Specifies that a following d, i, o, u, x, or X conversion
specifier applies to a short int or unsigned short int argument
(the argument will have been promoted according to the integer
promotions, but its value shall be converted to short int or
unsigned short int before printing);
[...]

and p9 says:

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

But is the "correct type" for "%hd" short, or is it int?

If the correct type is short, then the following two calls:

printf("%hd\n", (short)42);
printf("%hd\n", 42);

can behave differently, and the latter invokes undefined behavior.
For any user-written variadic function that uses <stdarg.h>, the two
calls are identical; the "(short)42)" argument is promoted to int
before the call.

The standard doesn't say that printf() uses <stdarg.h>. Conceivably
the compiler could treat printf() specially, but it would be difficult
(but not impossible) to do this for calls through a pointer, where the
compiler doesn't know whether it's calling printf() or an equivalent
user-defined variadic function.

Things would be more consistent if the "correct type" for "%hd" were
int rather than short.

Possibly they did it because it was simpler than making it undefined for
integers outside the range of short (the library might take advantage of
the limited range to do something that won't work with a larger range)
and undefined to pass int instead of long or long instead of int etc.
After all, why would you use %hd when you are actually going to be
passing an int that could be out of range for a short?
 
S

Serve Laurijssen

0
Keith Thompson said:
Specifies that a following d, i, o, u, x, or X conversion
specifier applies to a short int or unsigned short int argument
(the argument will have been promoted according to the integer
promotions, but its value shall be converted to short int or
unsigned short int before printing);
Things would be more consistent if the "correct type" for "%hd" were
int rather than short.

You're making it more complex than things are IMO.
You can just think of it that the variable is cast to short inside printf
discarding the bits that dont fit into a short anymore. So passing a short
is always safe, passing an int is safe as long as its value fits into a
short too.

Dont think I'll ever run into trouble on whatever platform with that in
mind.
 
C

CBFalconer

Keith said:
.... snip ...

Things would be more consistent if the "correct type" for "%hd"
were int rather than short.

It can't be, because the default promotions ensure that it will
always become an int.

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
 
K

Keith Thompson

Serve Laurijssen said:
You're making it more complex than things are IMO.

I'm not; the standard is.
You can just think of it that the variable is cast to short inside printf
discarding the bits that dont fit into a short anymore. So passing a short
is always safe, passing an int is safe as long as its value fits into a
short too.

Casting from int to short doesn't necessarily just discard bits, but
converting an int value that's representable as a short is guaranteed
to yield the same value.
Dont think I'll ever run into trouble on whatever platform with that in
mind.

That's probably true. Assuming that short is the "correct type" for
"%hd" (as the standard implies), passing an int value to printf() with
a "%hd" format invokes undefined behavior, according to the standard.
But it's difficult to imagine a conforming implementation in which

printf("%hd\n", 42);

does anything other than printing "42".
 
K

Keith Thompson

CBFalconer said:
It can't be, because the default promotions ensure that it will
always become an int.

Are you saying it can't be short, or it can't be int?

See also my article in comp.std.c, subject 'printf("%hd\n", 42);'.
 
C

CBFalconer

Keith said:
Are you saying it can't be short, or it can't be int?

Due to default promotion the actual parameter that the printf
routine receives will be an int if the parameter in the calling
routine is either an int or a short. Thus the %hd can't describe a
short, it can only describe special treatment for an int.

--
"The mere formulation of a problem is far more often essential
than its solution, which may be merely a matter of mathematical
or experimental skill. To raise new questions, new possibilities,
to regard old problems from a new angle requires creative
imagination and and marks real advances in science."
-- Albert Einstein
 
R

Robert Gamble

You caused undefined behaviour by not passing an argument of
type 'short int' to %hd
Interesting.

In the description of fprintf(), C99 7.19.6.1p7 says that the 'h'
modifier

Specifies that a following d, i, o, u, x, or X conversion
specifier applies to a short int or unsigned short int argument
(the argument will have been promoted according to the integer
promotions, but its value shall be converted to short int or
unsigned short int before printing);
[...]

and p9 says:

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

But is the "correct type" for "%hd" short, or is it int?
int.

If the correct type is short, then the following two calls:

printf("%hd\n", (short)42);
printf("%hd\n", 42);

can behave differently, and the latter invokes undefined behavior.
For any user-written variadic function that uses <stdarg.h>, the two
calls are identical; the "(short)42)" argument is promoted to int
before the call.

The standard doesn't say that printf() uses <stdarg.h>.

But it does specify the prototype which ends with the ellipsis
punctuator and the Standard specifies what happens to arguments passed
to such a function with a prototype in scope (specifically 6.5.2.2p7):
integer promotions are performed, regardless of whether printf is
implemented using stdarg.h or not, no two ways about it.
Conceivably
the compiler could treat printf() specially, but it would be difficult
(but not impossible) to do this for calls through a pointer, where the
compiler doesn't know whether it's calling printf() or an equivalent
user-defined variadic function.

Things would be more consistent if the "correct type" for "%hd" were
int rather than short.

I don't see how the parenthetical comment you cited from 7.19.6.1p7
leaves any room for doubt about this. Even if it could be argued that
it isn't crystal clear, it should be obvious that there is no way to
bypass the default argument promotions that occur in a call to a
variable-argument function regardless of how that function is
implemented, leaving no room for an alternate interpretation in the
first place.

Robert Gamble
 
K

Keith Thompson

CBFalconer said:
Due to default promotion the actual parameter that the printf
routine receives will be an int if the parameter in the calling
routine is either an int or a short. Thus the %hd can't describe a
short, it can only describe special treatment for an int.

I like the conclusion, but I'm not convinced by the logic. The
required behavior of printf depends heavily on what the "correct type"
is for a given conversion specification. If the "correct type" for
"%hd" is supposed to be int, the standard should say so explicitly.

Given the current description, even assuming int is the "correct
type", the behavior of printf("%hd", SHRT_MAX+1) (assuming
INT_MAX > SHRT_MAX) apears to be undefined by omission. That's
permitted, of course, but I'm not comfortable with it, and I don't
believe it was the intent.

See also the thread in comp.std.c.
 
U

user923005

What difference?

Or do you mean "couldn't care less" ?


brought scanf into this.

just say so, or don't reply)- Hide quoted text -- Show quoted text -

Of course its relevant. The printf() function is varadic, which means
that it is literally impossible to pass a short (which will promote to
int) or a float (which will promote to double) before it gets to the
called function, by default argument promotions. That is why printf()
doesn't care. It literally *can't* care about it because it will never
get a short and it will always get an int.

If you specify %hd then (internally) the printf() function should do
this:
short short_value = (short) int_value;
before printing it (in my opinion).
Of course, if you actually passed a short, then it will not be
different.
On the other hand, if you did not pass a short then the behavior is
undefined.

If you pass in the int value 32000 or if you pass in the short value
32000 there is literally no way that printf() can tell which one of
those two things you did.
 
R

Richard Bos

Keith Thompson said:
I like the conclusion, but I'm not convinced by the logic. The
required behavior of printf depends heavily on what the "correct type"
is for a given conversion specification. If the "correct type" for
"%hd" is supposed to be int, the standard should say so explicitly.

It does:

# h Specifies that a following d, i, o, u, x,orX conversion specifier
# applies to a short int or unsigned short int argument (the argument
# will have been promoted according to the integer promotions, but
# its value shall be converted to short int or unsigned short int
# before printing);

That's 7.19.6.1#7 from n1124.pdf. Note parenthesis.

Richard
 
S

Serve Laurijssen

Keith Thompson said:
I'm not; the standard is.

Thats what I mean, sometimes one doesnt need to read the standard to produce
code because that just complicates simple matters
 
R

Robert Gamble

# h Specifies that a following d, i, o, u, x,orX conversion specifier
# applies to a short int or unsigned short int argument (the argument
# will have been promoted according to the integer promotions, but
# its value shall be converted to short int or unsigned short int
# before printing);

That's 7.19.6.1#7 from n1124.pdf. Note parenthesis.

After reading the comp.std.c thread I finally understand Keith's point.
The problem is with the wording stating that the argument must be a
short int or unsigned short int (which will obviously undergo
promotion) but it doesn't accommodate an argument that starts out as an
int. I think the intent is obvious, and at least one person from the
Committee has already confirmed this on the comp.std.c thread, but the
wording in the descriptions of this function and others (all of the
ctype.h functions have a similar issue, there are probably others)
should be changed to reflect this. For example, instead of saying
"applies to a short int or unsigned short int argument" it should say
"applies to an int or unsigned int argument after any applicable
promotions" which would also eliminate the need for the parenthetical
comment.

Robert Gamble
 
K

Keith Thompson

Serve Laurijssen said:
Thats what I mean, sometimes one doesnt need to read the standard to
produce code because that just complicates simple matters

One *should* be able to read the standard to determine whether a given
piece of code is valid, and if so, what it does. I'm just pointing
out an instance where this doesn't necessarily work, due to a flaw in
the standard.

Ideally, if reading the standard "complicates simple matters", it's
likely because the matters really aren't as simple as you thought they
were.

Not all programmers *have* to read the standard, but you seem to be
arguing that they *shouldn't*. I may still be misunderstanding your
point. I strongly disagree with the point I *think* you're making.
 
S

Serve Laurijssen

Keith Thompson said:
One *should* be able to read the standard to determine whether a given
piece of code is valid, and if so, what it does. I'm just pointing
out an instance where this doesn't necessarily work, due to a flaw in
the standard.

Ideally, if reading the standard "complicates simple matters", it's
likely because the matters really aren't as simple as you thought they
were.

Not all programmers *have* to read the standard, but you seem to be
arguing that they *shouldn't*. I may still be misunderstanding your
point. I strongly disagree with the point I *think* you're making.

Nah, it shouldnt have been in this particular case. The issue was clear 10
posts ago but then the standard was brought in and it goes on and on and on.
I know, it's some people's hobby (or work) here to know all the small
details and nitpick about them and yes, the standard's wording tries to take
care of everything so it gets naturally complex. Just leave it out when it's
not needed :)
 
K

Keith Thompson

Serve Laurijssen said:
Nah, it shouldnt have been in this particular case. The issue was clear 10
posts ago but then the standard was brought in and it goes on and on and on.
I know, it's some people's hobby (or work) here to know all the small
details and nitpick about them and yes, the standard's wording tries to take
care of everything so it gets naturally complex. Just leave it out when it's
not needed :)

Feel free not to participate in discussions that you find boring.
 

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
474,434
Messages
2,571,691
Members
48,796
Latest member
Greg L.

Latest Threads

Top