Why doesn't gcc -Wformat warn about printf %d on an unsigned int?
Asked Answered
T

1

11

The following program has undefined behavior:

#include <stdio.h>

int main(void)
{
    unsigned int x = -100; // This is fine, becomes UINT_MAX - 100
    printf("%d\n", x); // This is undefined behavior.
    return 0;
}

C99 7.19.6.1p8 states %d expects an int argument.

C99 7.19.6.1p9 states "If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."

However, gcc -Wformat (which is included with -Wall) will not complain about the above program, why? Is this a bug, or a deliberate omission?

From the gcc manpage:

-Wformat

Check calls to "printf" and "scanf", etc., to make sure that the arguments supplied have types appropriate to the format string specified, and that the conversions specified in the format string make sense

Twoway answered 3/7, 2012 at 2:39 Comment(9)
C99 6.3.1.3p3 says conversion of unsigned to signed is implementation defined.Potboiler
@user315052: There is no conversion; the representation of x (an unsigned int object) is interpreted as if it were of type int.Pricking
@KeithThompson: I think there is because of C99 7.15.1.1p2, last sentence, where it makes an exception for signed/unsigned when converting argument types through the va_arg macro.Potboiler
@KeithThompson there is no conversion emitted in the code only because the function is varargs. If the function was declared to take int, the call would be legal. So why should gcc be expected to issue a warning for a legal case?Nagano
@user315052: I just read that paragraph; it doesn't imply that there's a conversion.Pricking
@AndyRoss: gcc of course isn't required to issue a warning, but it might reasonably do so since, if the value of x exceeds INT_MAX, the behavior is undefined. Arguments of type int and unsigned int are interchangeable only for values representable in both types.Pricking
@KeithThompson: It does deny it being undefined behavior. So it must be one of defined, unspecified, or implementation defined. The whole section was about converting types through va_arg. Since the standard doesn't specify the behavior in anyway other than through unsigned to signed conversion, I concluded implementation defined via that conversion. But, YMMV.Potboiler
@user315052: It's defined if "one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types"; in the example, UINT_MAX-100 exceeds INT_MAX. And the section says nothing about conversions.Pricking
Compilers aren't mandated to warn for anything that is UB in the standard. If this compiler is coded such that it behaves well in this case then there's no problem.Rayfordrayle
S
10

My best guess is that the warning is skipped because the UB is arguably invoked by the value and not merely by the type. va_arg allows the signedness to mismatch as long as the value is representable in both the signed and unsigned type. However, printf and friends are not specified in terms of va_arg and the standard states that any type mismatch results in UB, but this is probably a bug in the standard. Otherwise, printf("%x",1); would invoke UB. See my question on the topic:

Does printf("%x",1) invoke undefined behavior?

Scarface answered 3/7, 2012 at 2:54 Comment(5)
Thanks. That makes sense as 6.2.5p6 requires int and unsigned int to use the same amount of storage. Even though printf %d on an unsigned is technically undefined, there's no plausible reason for it to cause real issues.Twoway
The standard doesn't say that printf() uses <stdarg.h>, but the fact that you can construct a pointer to the printf() function can call through it implies at least some commonality in the argument-passing mechanism.Pricking
GCC 5.0 has such a warning now: -Wformat-signedness (enabled by -Wformat).Towbin
experimenting here rextester.com/live/CCQJKP79309 suggests that -Wformat-signedness works but is not included in -WformatPuebla
The whole point of being a separate option is that it doesn't.Scarface

© 2022 - 2024 — McMap. All rights reserved.