10

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

Destructor
  • 14,123
  • 11
  • 61
  • 126
Chris Young
  • 15,627
  • 7
  • 36
  • 42
  • 2
    C99 6.3.1.3p3 says conversion of unsigned to signed is implementation defined. – jxh Jul 03 '12 at 03:14
  • @user315052: There is no conversion; the representation of `x` (an `unsigned int` object) is interpreted as if it were of type `int`. – Keith Thompson Jul 03 '12 at 03:32
  • @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. – jxh Jul 03 '12 at 04:01
  • @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? – Andy Ross Jul 03 '12 at 04:32
  • @user315052: I just read that paragraph; it doesn't imply that there's a *conversion*. – Keith Thompson Jul 03 '12 at 06:57
  • @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. – Keith Thompson Jul 03 '12 at 06:59
  • @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. – jxh Jul 03 '12 at 07:07
  • @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. – Keith Thompson Jul 03 '12 at 08:27
  • 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. – M.M Jun 14 '16 at 13:47

1 Answers1

9

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?

Community
  • 1
  • 1
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 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. – Chris Young Jul 03 '12 at 03:09
  • The standard doesn't say that `printf()` uses ``, 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. – Keith Thompson Jul 03 '12 at 07:02
  • 3
    GCC 5.0 has such a warning now: [`-Wformat-signedness`](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60194) (enabled by `-Wformat`). – cremno Mar 18 '15 at 12:24
  • experimenting here http://rextester.com/live/CCQJKP79309 suggests that -Wformat-signedness works but is not included in -Wformat – MK. Apr 03 '17 at 21:27
  • The whole point of being a separate option is that it doesn't. – R.. GitHub STOP HELPING ICE Apr 04 '17 at 08:34