6

When I executed the code of this question, I got this warning:

warning: format '%d' expects argument of type 'int', but argument 2 has type 'long int' [-Wformat=]
printf("P-Q: %d, P: %d, Q: %d", (p - q), p, q);
             ~^                 ~~~~~~~
             %ld

As a reflex fix, I used %ld to print the subtraction of two pointers. And the compiler agreed.

Fortunately, I saw a comment from another user mentioning that %td should be used, since the result type of the subtraction is ptrdiff_t. This answer confirms this claim.

Now from GCC's header file of stddef.h, I can see that these types are equivalent in this case:

typedef __PTRDIFF_TYPE__ ptrdiff_t;
#define __PTRDIFF_TYPE__ long int

However, I was just going to suggest a wrong (more or less) fix to the OP, with %ld, instead of %td.

Is there a way I could have understood that the compiler warning alone was not enough? Or maybe to wisely have interpreted the warning itself, and not just react.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 3
    Seems like a QoI issue. A compiler should be able to detect it's two pointers being subtracted, and print `ptrdiff_t` instead of what it aliases. – StoryTeller - Unslander Monica Jun 27 '18 at 08:21
  • Both g++ (if it makes sense to compile with it too) and gcc provided the same warning @StoryTeller, if it helps. – gsamaras Jun 27 '18 at 08:26
  • 4
    Live and learn. I have yet to see a compiler warning that mentions `%ju` or `%zu` or `%tu`. The warnings always mention the basic types that the implementation uses. So you just have to know... – user3386109 Jun 27 '18 at 08:30
  • 2
    [Clang](https://wandbox.org/permlink/u8QLyLtEXHyhayU1) suggests `%ld` too... – Bob__ Jun 27 '18 at 08:36
  • 1
    user3386109 experience sure. Wisdom and experience walk together..Good to know @Bob__ , thank you! – gsamaras Jun 27 '18 at 08:48
  • @StoryTeller *A compiler should be able to detect it's two pointers being subtracted* It's probably not that easy - by the time the compiler reaches the point it's actually generating code, the parser has replaced `ptrdiff_t` with the underlying base type. Given how `ptrdiff_t` was bolted onto C long after initial language development, any long-extant compiler would probably need a rewrite or some hideous hackery to detect that. – Andrew Henle Jun 27 '18 at 09:45
  • @Bob__ not quite so. It just happens to be `%ld` because `ptrdiff_t` was `typedef`ed to `long` in 64-bit mode. [Compile with `-m32` or `-mx32` to see the difference](https://godbolt.org/g/dcBv2r). Wandbox somehow always convert `-m32` to `"-m32"` or probably don't support those options so I have to use godbolt for that – phuclv Jun 28 '18 at 02:50
  • @phuclv Yes, but OP's concern still holds, the compiler is accepting `%d` instead of `%td`. – Bob__ Jun 28 '18 at 08:36
  • @Bob__ no, the format string is not handled in anyway by the compiler. It's passed directly to the `printf` although a smart compiler may understand some parts of the format string to give useful warnings. The compiler cannot flag those as errors, but it doesn't mean that the behavior is defined – phuclv Jun 28 '18 at 08:45

2 Answers2

2

I don't think you can tell. It depends on the intent/caution/smartness of the compiler writer.

Maybe he decided he would always support %ld where %td is expected, or maybe he was just unaware/unable/unwilling to give a more detailed/proper message. In case of doubt, your last resort is the standard.

This doesn't seem to be a portable construct and for "orthodoxy" you should support both format specifiers.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
2

The key here is: don't do any form of arithmetic inside printf in the first place. Separate algorithm from GUI.

Code such as printf("%d", p - q) is very dangerous, not just because you might get the types wrong logically, but also since C might "do you a favour" and silently change the types through implicit type promotion. Examples.

In addition, most compilers don't warn for wrong format specifiers. This is a relatively new thing in the history of C, since compilers aren't required to show a diagnostic message here. It is just a bonus feature of gcc.

How to avoid bugs? These functions are inherently dangerous - that's just how it is and everyone knows it. Probably printf & scanf family of functions are the most harmful functions ever written in the history of programming, in terms of total bug cost caused to mankind. So what you should do to:

  • Avoid stdio.h if possible and keep it away from production-quality code. Portability is not always more important than robust code - sometimes it is preferable to use the raw console API. Avoid variable argument list functions in general.
  • If not possible to avoid, wrap the "GUI" part of stdio.h inside a separate file, which you should be doing anyway. Don't mix printing/input with algorithms. Make an interface which is using pointers.
  • It is 2018, not 1970: don't write console interfaces in the first place. Ye ye I know... there's lots of old crap still floating around which needs to be maintained. But nowadays, console functions should be used mostly for debugging purposes and for newbies learning C, in which case type safety might not be such a big issue.
Lundin
  • 195,001
  • 40
  • 254
  • 396