14

I was (quickly) writing some code and accidently inverted the arguments in scanf():

char i[] = "ABC1\t";
scanf(i, "%s");

Compiling with gcc -Werror -Wall -Wextra doesn't complain about this one bit. Obviously, this code doesn't work, but why didn't gcc inform me that I inverted the arguments? Can't it detect that i is not a format string, or that the second argument was not a store-able type?

EDIT
Thanks for the insight all, Looks like I found the answer, there was a twist on the -Wformat flag that makes this "catchable" (posted it below for reference)

Mike
  • 47,263
  • 29
  • 113
  • 177

3 Answers3

18

Ha! I found it. Hitting gcc with the -Wformat=2 flag caught it.

Posting the info for reference of others:

Here's the list of flags I found

-Wformat Check calls to printf and scanf, etc., to make sure that the arguments supplied have types appropriate to the format string specified...

I had assumed -Wall had -Wformat in it, which it does, but the really important part about what I just found:

-Wformat is included in -Wall. For more control over some aspects of format checking, the options -Wformat-y2k, -Wno-format-extra-args, -Wno-format-zero-length, -Wformat-nonliteral, -Wformat-security, and -Wformat=2 are available, but are not included in -Wall.

Mike
  • 47,263
  • 29
  • 113
  • 177
8

I suppose it shouldn't.

int scanf ( const char * format, ... );

i was normally converted to a const char*, all the rest parameters are just "ellipsis" and cannot be checked at compile time.

Lyth
  • 2,171
  • 2
  • 29
  • 37
  • 1
    The headers used by GCC usually have the first parameter marked as a format string and then it can ensure that the correct types are passed to the remaining parameters, provided `-Wformat` is provided. I believe this is part of `-Wall`. – Will Oct 25 '12 at 12:39
  • 1
    @Will But then again - a non-const char* was passed as a format string (which is valid by itself) and so compiler did not force parameter check. It might have done so if format string was `const char*` or literal. – Lyth Oct 25 '12 at 12:43
  • Probably it does not check between 'char* fmt' & 'const char* fmt'. Can anyone tell, if the second argument ("ABC\t") will be char* or const char*? – anishsane Oct 25 '12 at 12:47
  • EDIT: Probably it does not check between 'char* fmt' & 'const char* fmt'. Can anyone tell, if the second argument ("ABC\t") will be char* or const char*? btw, 2nd argument will be completely ignores, because the format string does not contain any % format character. – anishsane Oct 25 '12 at 12:53
  • The thing is that glibc defines `scanf` with `__attribute__((format(scanf, 1, 2)))` which is a gcc option to tell it to check the arguments and issue a warning. Otherwise, it is obvious that the question doesn't have a type mismatch error. – Shahbaz Oct 25 '12 at 13:32
3

The manual entry for scanf (man scanf) gives the prototype:

int scanf(const char *format, ...);

A char[] is just a special type of char *, so the first argument is satisfied. Secondary arguments are evaluated at runtime (if I recall), so they aren't even considered by the compiler here. From the compiler's prospective, this is a fine call to the function given its prototype.

Also, the compiler never checks whether you are trying to write to invalid locations. The great (or terrible) thing about C is that it will let you do more or less what you want, even if what you want is a bad idea.

John Doucette
  • 4,370
  • 5
  • 37
  • 61
  • 2
    It is true that `scanf` by itself accepts these, but in glibc, compiled with gcc, scanf has `__attribute__((format(scanf, 1, 2)))` which tells the compiler to make sure the parameters are correct, even though this is not enforced by the declaration of `scanf`. – Shahbaz Oct 25 '12 at 13:30