printf length modifier

S

Spoon

Hello,

Does the following code invoke undefined behavior?

#include <stdio.h>
int main(void)
{
unsigned short int u = 42;
printf("%u\n", u);
return 0;
}

gcc doesn't seem to mind.
$ gcc -Wall -Wextra -std=c89 -pedantic zzz.c
$ ./a.out
42

Is it mandatory to add the 'h' length modifier in the format string?

unsigned short int u = 42;
printf("%hu\n", u);

Regards.
 
C

CBFalconer

Spoon said:
Does the following code invoke undefined behavior?

#include <stdio.h>
int main(void)
{
unsigned short int u = 42;
printf("%u\n", u);
return 0;
}
Yes.


gcc doesn't seem to mind.
$ gcc -Wall -Wextra -std=c89 -pedantic zzz.c
$ ./a.out
42

Is it mandatory to add the 'h' length modifier in the format string?

Yes. Add the -O (capital letter) option to the gcc run.
 
S

Spoon

CBFalconer said:
Yes. Add the -O (capital letter) option to the gcc run.

$ gcc -O -Wall -Wextra -std=c89 -pedantic zzz.c
$ ./a.out
42

$ gcc -O3 -Wall -Wextra -std=c89 -pedantic zzz.c
$ ./a.out
42

:)

Aren't short int parameters "widened" to int in variadic function calls?

Regards.
 
F

Flash Gordon

Spoon wrote, On 26/04/07 10:33:
Hello,

Does the following code invoke undefined behavior?

#include <stdio.h>
int main(void)
{
unsigned short int u = 42;
printf("%u\n", u);
return 0;
}

Is it mandatory to add the 'h' length modifier in the format string?

unsigned short int u = 42;
printf("%hu\n", u);

unsigned short is likely to be promoted to int rather than signed int,
so using %u is lying. Anyway, why would you want to use a possibly
incorrect format specifier when you know the definitely correct one?
 
F

Flash Gordon

Spoon wrote, On 26/04/07 12:20:
Aren't short int parameters "widened" to int in variadic function calls?

You are using unsigned short, which could be widened to int (most
probable) or unsigned int, and using the format specifier for an
unsigned int. int and unsigned int are NOT the same thing.

There is also the big question of why you do not want to use what you
know is definitely the correct format specifier.
 
S

Spoon

Flash said:
Spoon wrote, On 26/04/07 12:20:



You are using unsigned short, which could be widened to int (most
probable) or unsigned int, and using the format specifier for an
unsigned int. int and unsigned int are NOT the same thing.

(Hair-splitting ahead) AFAIU, for the value 42, int and unsigned int are
equivalent because both conversions are well-defined.
There is also the big question of why you do not want to use what you
know is definitely the correct format specifier.

u is, in fact, an uint16_t and I don't know for sure that u is an
unsigned short int. It might be an unsigned int.

Regards.
 
F

Flash Gordon

Spoon wrote, On 26/04/07 14:31:
(Hair-splitting ahead) AFAIU, for the value 42, int and unsigned int are
equivalent because both conversions are well-defined.

True, but it is reasonable to assume that in the real code other values
will be used. It is not unreasonable to expect that some of those values
will be too large to fit in a signed short, or why bother with the extra
typing?
u is, in fact, an uint16_t and I don't know for sure that u is an
unsigned short int. It might be an unsigned int.

If this is C99 code then I believe there is a macro provided in C99 to
solve this problem. Otherwise, cast the value to a known type for printing.
printf("%u\n", (unsigned int)u);
Or, alternatively, as you know that any value that will fit in an
unsigned 16 bit integer will fit in an unsigned short (which may be more
than 16 bits, but not less), cast to unsigned short and use the h modifier.
 
G

Guest

Spoon said:
(Hair-splitting ahead) AFAIU, for the value 42, int and unsigned int are
equivalent because both conversions are well-defined.

Huh? I really don't understand what you're trying to say here. Could
you please explain?
u is, in fact, an uint16_t and I don't know for sure that u is an
unsigned short int. It might be an unsigned int.

The format specifier for uint16_t is PRIu16.

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

#ifndef UINT16_MAX
#error uint16_t not supported
#endif

int main(void)
{
uint16_t u = 42;
printf("%" PRIu16 "\n", u);
return 0;
}
 
F

Flash Gordon

Harald van Dijk wrote, On 26/04/07 18:51:
Spoon wrote:


Huh? I really don't understand what you're trying to say here. Could
you please explain?

The standard guarantees that the representation of all positive values
of int is the same as the representation of those values in an unsigned
int. At least, I assume that is what Spoon meant, although if so it
could be phrased better.
 
G

Guest

Flash said:
Harald van Dijk wrote, On 26/04/07 18:51:

The standard guarantees that the representation of all positive values
of int is the same as the representation of those values in an unsigned
int. At least, I assume that is what Spoon meant, although if so it
could be phrased better.

Thanks for the explanation. I don't think there's any prohibition
against an implementation passing the object type along with the
value, and bombing out if the passed type does not exactly match the
expected type for printf(), but only bombing out if the passed type
does not loosely match the expected type for va_arg(). Aside from an
extremely strict debugging implementation though, I can't imagine why
anyone would want to do that.
 
K

Keith Thompson

Spoon said:
(Hair-splitting ahead) AFAIU, for the value 42, int and unsigned int
are equivalent because both conversions are well-defined.

Conversion has nothing to do with it. Conversion between int and
double is well-defined, but passing a double argument for a "%d"
format invokes undefined behavior. No conversion, other than argument
promotions, occurs for variadic arguments.

It's true that the value 42 is guaranteed to have the same
*representation* in types int and unsigned int. Specifically, C99
6.2.5p9 says:

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.

So you can look carefully through the standard for proof that the code
will work properly whether the argument is promoted to int or to
unsigned int (note that this depends on a footnote, which is
nonnormative). Note that you'll also need to depend on the
implementer getting this right. It's also likely that the argument
will be promoted to int on all the platforms you use, so you'll never
have a chance to test the case where it's promoted to unsigned int,
just in case you missed something.

Or you can write simpler code that doesn't depend on a potentially
shaky line of reasoning.
u is, in fact, an uint16_t and I don't know for sure that u is an
unsigned short int. It might be an unsigned int.

So convert it to unsigned int and use "%u".

Incidentally, there's another subtle issue here. The standard's
description of printf() describes the required formats for various
types. It's almost certainly safe to assume that printf() uses the
same mechanisms as a user-written variadic function, so you can safely
assume that argument promotion occurs as you would expect. But the
standard doesn't *quite* come out and say so. (There was a lengthy
discussion of this in comp.std.c a while ago.) I don't think it's
anything to worry about, but it's enough to make me more cautious with
format strings than I absolutely need to be.
 
J

Jack Klein

Thanks for the explanation. I don't think there's any prohibition
against an implementation passing the object type along with the
value, and bombing out if the passed type does not exactly match the
expected type for printf(), but only bombing out if the passed type
does not loosely match the expected type for va_arg(). Aside from an
extremely strict debugging implementation though, I can't imagine why
anyone would want to do that.

Actually there is such a prohibition, 6.5.2.2 p6. It applies to both
functions without prototypes and to the variable arguments of variadic
functions:

<begin>
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.
<end>

So it makes no difference if the unsigned short is promoted to signed
or unsigned int, the actual int object has exactly the same bit
representation and is passed the same way.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
G

Guest

Jack said:
Actually there is such a prohibition, 6.5.2.2 p6.

6.5.2.2p6 describes function calls where the function declaration
lacks a prototype. Any definition of printf must include a prototype.
6.5.2.2p7 describes function calls where the function declaration
includes a prototype, and contains no exception similar to what you
quoted.
 

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

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top