Usefulness of %hd in printf

O

Old Wolf

Is there any possible situation for printf where %hd causes a different
result to %d, and the corresponding argument was of type 'short int' ?
 
U

user923005

Old said:
Is there any possible situation for printf where %hd causes a different
result to %d, and the corresponding argument was of type 'short int' ?

Think about the scanf() family of functions.

Sure, the short will promote just like floats promote to double for the
same reason. But addresses do not promote. Hence, the need for a
width specifier for 'smaller than default promotion' types.
 
O

Old Wolf

user923005 said:
Think about the scanf() family of functions.

I'm asking about the printf() family.

Are you saying that there is no difference in printf, but they added
support for %hd just to try and keep the printf family similar to the
scanf family?
 
U

user923005

Old said:
I'm asking about the printf() family.

Are you saying that there is no difference in printf, but they added
support for %hd just to try and keep the printf family similar to the
scanf family?

No. I am saying scanf() has to have it, while printf() could care
less.

We could just as easily complain about:

12.9: Someone told me it was wrong to use %lf with printf(). How can
printf() use %f for type double, if scanf() requires %lf?

A: It's true that printf's %f specifier works with both float and
double arguments. Due to the "default argument promotions"
(which apply in variable-length argument lists such as
printf's, whether or not prototypes are in scope), values of
type float are promoted to double, and printf() therefore sees
only doubles. (printf() does accept %Lf, for long double.)
See also questions 12.13 and 15.2.

References: K&R1 Sec. 7.3 pp. 145-47, Sec. 7.4 pp. 147-50; K&R2
Sec. 7.2 pp. 153-44, Sec. 7.4 pp. 157-59; ISO Sec. 7.9.6.1,
Sec. 7.9.6.2; H&S Sec. 15.8 pp. 357-64, Sec. 15.11 pp. 366-78;
CT&P Sec. A.1 pp. 121-33.
 
U

user923005

From the C99 standard, here is the bit on default promotions for things
lacking prototypes and for variadic functions...
From ©ISO/IEC ISO/IEC 9899:1999 (E), 6.5.2.2 Function calls:

"6 If the expression that denotes the called function has a type that
does not include a prototype, the integer promotions are performed on
each argument, and arguments that have type float are promoted to
double. These are called the default argument promotions. If the number
of arguments does not equal the number of parameters, the behavior is
undefined. If the function is defined with a type that includes a
prototype, and either the prototype ends with an ellipsis (, ...) or
the types of the arguments after promotion are not compatible with the
types of the parameters, the behavior is undefined. If the function is
defined with a type that does not include a prototype, and the types of
the arguments after promotion are not compatible with those of the
parameters after promotion, the behavior is undefined, except for the
following cases:
* one promoted type is a signed integer type, the other promoted type
is the corresponding unsigned integer type, and the value is
representable in both types;
* both types are pointers to qualified or unqualified versions of a
character type or void."
 
O

Old Wolf

user923005 said:
No. I am saying scanf() has to have it, while printf() could care
less.

So you are saying it does make a difference to printf?
What difference?

Or do you mean "couldn't care less" ?
We could just as easily complain about:

12.9: Someone told me it was wrong to use %lf with printf(). How can
printf() use %f for type double, if scanf() requires %lf?

Huh? I'm not complaining. I'm asking about printf (NOT scanf). You
brought scanf into this.
A: It's true that printf's %f specifier works with both float and
double arguments. Due to the "default argument promotions"
(which apply in variable-length argument lists such as
printf's, whether or not prototypes are in scope), values of
type float are promoted to double, and printf() therefore sees
only doubles. (printf() does accept %Lf, for long double.)
See also questions 12.13 and 15.2.

This is irrelevant to my question. (If you don't know the answer then
just say so, or don't reply)
 
P

P.J. Plauger

So you are saying it does make a difference to printf?
What difference?

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


Huh? I'm not complaining. I'm asking about printf (NOT scanf). You
brought scanf into this.


This is irrelevant to my question. (If you don't know the answer then
just say so, or don't reply)

%hd prints out the int argument cast to short. So it scrapes off any bits
that can't be represented in a short.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
Y

Yevgen Muntyan

P.J. Plauger said:
%hd prints out the int argument cast to short. So it scrapes off any bits
that can't be represented in a short.

So the following is a bug?

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

int main (void)
{
int a = 1<<30;
short b = a;
printf ("%d %hd %hd\n", a, a, b);
return 0;
}
----------------------------

It prints

1073741824 1073741824 0

i.e. it doesn't truncate 'a' (which of type int) when prints
it with %hd.

Thanks,
Yevgen
 
O

Old Wolf

Yevgen said:
So the following is a bug?

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

int main (void)
{
int a = 1<<30;
short b = a;
printf ("%d %hd %hd\n", a, a, b);
return 0;
}
----------------------------

It prints

1073741824 1073741824 0

i.e. it doesn't truncate 'a' (which of type int) when prints
it with %hd.

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

Old Wolf

P.J. Plauger said:
%hd prints out the int argument cast to short. So it scrapes off any bits
that can't be represented in a short.

Well, when the short int is passed as argument, it is promoted to int
(according to the default argument promotions). So how can the int
have any bits that can't be represented in a short?

#include <stdio.h>
int main()
{
short x;
/* your code here */
printf("%hd\n%d\n", x, x);
return 0;
}

Is there any possible code you can insert where marked, that will
cause two different values to be printed (and the program to not
have undefined behaviour) ?
 
P

P.J. Plauger

So the following is a bug?

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

int main (void)
{
int a = 1<<30;
short b = a;
printf ("%d %hd %hd\n", a, a, b);
return 0;
}
----------------------------

It prints

1073741824 1073741824 0

i.e. it doesn't truncate 'a' (which of type int) when prints
it with %hd.

Yes, it's a bug. From the C Standard, for 'h' in printf:

: h -- 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); or that a following n
: conversion specifier applies to a pointer to a short int
: argument.

Our library prints:

1073741824 0 0

which I believe is correct.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
Y

Yevgen Muntyan

Old said:
You caused undefined behaviour by not passing an argument of
type 'short int' to %hd

Hm, good point. So P.J.Plaguer was talking about UB or about
casting short to short?
I would love to ask now about what happens in practice when
you pass ints to printf, but I won't, since I would flamed
real hard if I did (like, int a = 8; printf("%hd", a); -
real code can be like that, maybe not with short but with
size_t or something).

Yevgen
 
B

Ben Pfaff

P.J. Plauger said:
Yes, it's a bug. From the C Standard, for 'h' in printf:

: h -- 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); or that a following n
: conversion specifier applies to a pointer to a short int
: argument.

I disagree. It's a bug in the test program, because the first
%hd argument is not a short int or unsigned short int argument,
which the description above require it to be. It's an int
argument.

If that is not correct, for some reason, then its behavior is
implementation-defined or perhaps undefined, because converting
an out-of-range value to a signed integer type either causes
implementation-defined behavior or raises an
implementation-defined signal.
 
P

P.J. Plauger

Hm, good point. So P.J.Plaguer was talking about UB or about
casting short to short?

The C Standard does suggest that the argument to %hd began
as a short and got promoted to int, but all printf cares
about is that it gets an int argument for %hd. The need for
a cast would be vacuous if only short were permitted, so that
suggests that any int value can be passed. Since the effect
of passing any int is defined, it's hard to make a case that
passing certain int values is undefined.
I would love to ask now about what happens in practice when
you pass ints to printf, but I won't, since I would flamed
real hard if I did (like, int a = 8; printf("%hd", a); -
real code can be like that, maybe not with short but with
size_t or something).

Don't see the need for a flame here -- this sort of thing
happens all the time. Some of the wording in the C Standard
reflects the looser heritage of pre-standard days, particularly
in the passing of int/unsigned int where short/unsigned short
is expected. In fact, an anticipated need for %hx was to
ensure that passing -1 prints as ffff (on an 8/16/32 bit
implementation), not ffffffff.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
P

P.J. Plauger

I disagree. It's a bug in the test program, because the first
%hd argument is not a short int or unsigned short int argument,
which the description above require it to be. It's an int
argument.

I think you can read the C Standard as requiring an int argument,
as I said in my previous post.
If that is not correct, for some reason, then its behavior is
implementation-defined or perhaps undefined, because converting
an out-of-range value to a signed integer type either causes
implementation-defined behavior or raises an
implementation-defined signal.

Now that's a different issue. I agree that the cast overflows,
so that part is undefined. But my remarks about %hx stand,
since conversion to unsigned short is well defined.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
F

Flash Gordon

P.J. Plauger wrote, On 23/01/07 14:45:
Yes, it's a bug. From the C Standard, for 'h' in printf:

: h -- 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); or that a following n
: conversion specifier applies to a pointer to a short int
: argument.

Our library prints:

1073741824 0 0

which I believe is correct.

I thought the conversion from int to short int was only defined when the
value of the int was in the range that can be represented as a short
int? This, of course, would leave your library as still conforming.

I fully expect to be proved wrong as I know you know the standard better
than I do ;-)
 
P

P.J. Plauger

P.J. Plauger wrote, On 23/01/07 14:45:

I thought the conversion from int to short int was only defined when the
value of the int was in the range that can be represented as a short int?
This, of course, would leave your library as still conforming.

I fully expect to be proved wrong as I know you know the standard better
than I do ;-)

Sorry, but you're right, as I pointed out in an earlier post.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com
 
C

CBFalconer

Yevgen said:
.... snip ...

So the following is a bug?

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

int main (void)
{
int a = 1<<30;
short b = a;

This probably overflows, and all other behaviour is undefined. In
fact the 1<<30 may have overflowed already. So you have no hope of
detecting anything in any further code.

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

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.
 
K

Keith Thompson

CBFalconer said:
This probably overflows, and all other behaviour is undefined. In
fact the 1<<30 may have overflowed already. So you have no hope of
detecting anything in any further code.

Assuming the value 1<<30 can be represented as an int but not as a
short, there is no undefined behavior. Overflow on an arithmetic
operator invokes UB, but overflow on a conversion, as in "short b = a;",
merely yields an implementation-defined result or (in C99) raises an
implementation-defined signal.

But yes, it's also possible that 1<<30 exceeds INT_MAX; in that case,
the "<<" operator invokes undefined behavior even before the result is
assigned to "a".
 

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

Latest Threads

Top