gcc auto correcting format in printf - c

I am using gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4 and compiling following code:
void main()
{
float f= 10.0;
char *str = "hello";
printf("f=%.1f str=%s\n",str, f);
}
Compiler is showing Warning that is expected:
prac.c: In function 'main':
prac.c:8:1: warning: format '%f' expects argument of type 'double', but argument 2 has type 'char *' [-Wformat=]
printf("f=%.1f str=%s\n",str, f);
^
prac.c:8:1: warning: format '%s' expects argument of type 'char *', but argument 3 has type 'double' [-Wformat=]
However, while executing this program, output is auto-corrected.
# ./a.out
f=10.0 str=hello
How this is happening.
Which feature of compiler is doing this.
Any way to disable this auto-correction.

That's not "auto correction," it's "undefined behavior." It's happening because floating-point variables may be (or may not be!) passed to functions in a separate space vs. integral types. So when you pass one pointer (or int) and one double, they are still using the first slot for each type, even though you specified them in the wrong order.
Compile with -Wformat=2 and -Werror and you will never see this problem again, because GCC (and Clang) will refuse to build such code.

Related

Why does GCC only complain about an unsigned int argument being used with %i when -pedantic is given?

sscanf(3) says (emphasis mine):
i
Matches an optionally signed integer; the next pointer must be a pointer to int. The integer is read in base 16 if it begins with 0x or 0X, in base 8 if it begins with 0, and in base 10 otherwise. Only characters that correspond to the base are used.
However GCC does not complain about using an unsigned int with %i unless -pedantic is given. This is different from the behavior I'm used to, where GCC will warn for any mismatched type and format string.
Why does this combination behave differently?
Given that this warning is not included in the common -Wall set of warnings, is it acceptable to pass unsigned int to %i?
Example program:
#include <stdio.h>
int main(void)
{
int i;
unsigned int u;
float f;
scanf("%i", &i);
scanf("%i", &u);
scanf("%i", &f);
return 0;
}
Without -pedantic, GCC complains about %i and float *, but not unsigned int *:
$ gcc -Wall -Wextra scanf_i.c
scanf_i.c: In function ‘main’:
scanf_i.c:11:13: warning: format ‘%i’ expects argument of type ‘int *’, but argument 2 has type ‘float *’ [-Wformat=]
scanf("%i", &f);
~^ ~~
%e
With -pedantic, GCC complains about both:
Output with -pedantic:
$ gcc -Wall -Wextra -pedantic scanf_i.c
scanf_i.c: In function ‘main’:
scanf_i.c:10:13: warning: format ‘%i’ expects argument of type ‘int *’, but argument 2 has type ‘unsigned int *’ [-Wformat=]
scanf("%i", &u);
~^ ~~
%i
scanf_i.c:11:13: warning: format ‘%i’ expects argument of type ‘int *’, but argument 2 has type ‘float *’ [-Wformat=]
scanf("%i", &f);
~^ ~~
%e
GCC version:
$ gcc --version
gcc (Debian 8.3.0-6) 8.3.0
-Wformat-signedness controls whether warnings are raised when the argument type differs only in signedness from what's expected.
From the gcc(1) man page:
-Wformat-signedness
If -Wformat is specified, also warn if the format string requires an unsigned argument and the argument is signed and vice versa.
The man page does not explicitly state that this flag is included in -pedantic, (-Wpedantic) but it does say:
However, if -Wpedantic is used with -Wformat, warnings are given about format features not in the selected standard version.

How is the linking done for string functions in C?

I tried to using two different functions from sting.h header file (without including it) strlen() and strtok(). Strlen executed successfully without any error (but some warnings), strtok failed at runtime. Why is it that strlen() function worknig gine but not strtok() if I don't include the header file? I suppose there is something in the linking process that I don't understand. Please clarify for such behavior. However, the program terminates successfully if I print a as '%c' instead of '%s' (strtok returns a garbage value).
CODE:
int main()
{
char string[] = "This is a String";
printf("\nLength of sting is %d\n",strlen(string));
}
WARNINGS:
hello.c: In function ‘main’:
hello.c:4:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
hello.c:4:37: warning: incompatible implicit declaration of built-in function ‘strlen’ [enabled by default]
hello.c:4:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat]
OUTPUT:
$ ./a.out
Length of sting is 16
CODE:
int main()
{
char string[] = "This is a String";
printf("\nLength of sting is %d\n",strlen(string));
char *a = strtok(string," ");
printf("%s",a);
}
WARNINGS:
$ gcc hello.c
hello.c: In function ‘main’:
hello.c:4:2: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
hello.c:4:37: warning: incompatible implicit declaration of built-in function ‘strlen’ [enabled by default]
hello.c:4:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat]
hello.c:5:12: warning: initialization makes pointer from integer without a cast [enabled by default]
OUTPUT:
$ ./a.out
Length of sting is 16
Segmentation fault (core dumped)
The linking is done implicitly for most of the C standard library. You just have to include the headers of what you use (that's the warnings).
When you don't, your compiler (which is compiling your code as ANSI C) assumes the functions return int, instead of their documented return types. Your program has undefined behavior because of that. Always heed warnings!
You must include the headers string.h and stdio.h.
The first program works because you were lucky enough in that strlen is supposed to return an integer (a size_t to be exact, but it appears on your system the value representation for integers and size_t lets the program slide through).
The second fails because strtok returns a char* but it's assumed an int due to your missing include directives. int and char* are not compatible types, so the "pointer" you get isn't valid.
Sting is a singer, not a C header... Still, the problem is that in C, when you use functions that were not declared, the compiler guesses them to have the types of the arguments you first pass to them and returning an int.
That's the reason why you get all these warnings: several undeclared functions you use don't actually have the deduced signature (although they are not too wrong, and end up working), and the real strtok returns a pointer, not an int (the warning in facts says that you are assigning an int to a pointer without a cast). The crash you are getting most probably comes from the fact that the original pointer is 64 bit, but is truncated to 32 bit when is erroneously considered an int before being assigned to a.
Still, the point to take home is: include headers and don't ever rely on implicit function declaration. It's a dangerous mess that exists only for backward compatibility reasons.

How printf works in case of type mismatch with type specifier?

int main()
{
printf("%c", "\n");
return 0;
}
Here according to type specifier a character is required. But we are passing it const char *. I was hoping that it would give me a warning message in code blocks GNU GCC compiler but it is not giving me any warning and printing the $ character.
why it is not giving any type of warning?
You need to enable that warning.
Compiling your code as test.cpp and adding #include <stdio.h> for printf with the correct warning flag under gcc 4.7.2:
$ gcc -c -Wformat test.cpp
test.cpp: In function 'int main()':
test.cpp:5:18: warning: format '%c' expects argument of type 'int', but argument 2 has type 'const char*' [-Wformat]
With printf() if there is a type mismatch then it leads to undefined behavior.
The format specifier should match with the type which you want to print.
Further the number of arguments should match with the number of format specifiers violating which will also leads to undefined behavior.
Just add -Wall while compiling your code you will get the below error:
warning: format %c expects type int, but argument 2 has type char *
You could see that the code also works with %d, %x, %u format specifiers.
Why it works without any warnings ?
Because you don't have warnings enabled in your CodeBlocks.
Go to settings -> compiler and check
Enable All Common Compiler Warnings [-Wall]
And now you get:
In function 'int main()':
warning: format '%c' expects argument of type 'int', but argument 2 has type 'const char*' [-Wformat=]|
Why it even works ?
With %c, $ is the output in CodeBlocks, X is the output in Visual Studio . So, that sounds like undefined behavior.
Wrong format specifiers in scanf (or) printf
Anyways if you want the first char this way only you could do this:
#include <stdio.h>
int main()
{
printf("%c", *"Hello\n"); // Not asked in Question but still :)
return 0;
}
It prints H by dereferencing the const pointer.

Why does GCC show duplicate warnings for bad printf format specifier?

I'm curious why GCC shows me two identical warnings when compiling this file:
$ cat test.c
#include <stdio.h>
int main (int argc, char const *argv[])
{
long foo = 0l;
printf("%i\n", foo);
return 0;
}
$ gcc-4.2 -Wall test.c
test.c: In function ‘main’:
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
Interestingly, Clang also gives two warnings:
$ clang test.c
test.c:6:14: warning: conversion specifies type 'int' but the argument has type 'long' [-Wformat]
printf("%i\n", foo);
~^ ~~~
%ld
test.c:6:14: warning: conversion specifies type 'int' but the argument has type 'long' [-Wformat]
printf("%i\n", foo);
~^ ~~~
%ld
2 warnings generated.
Any ideas?
For info:
$ gcc-4.2 -v
Using built-in specs.
Target: i686-apple-darwin11
Configured with: /private/var/tmp/gcc/gcc-5666.3~278/src/configure
--disable-checking --enable-werror --prefix=/usr --mandir=/share/man
--enable-languages=c,objc,c++,obj-c++
--program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib
--build=i686-apple-darwin11 --program-prefix=i686-apple-darwin11-
--host=x86_64-apple-darwin11 --target=i686-apple-darwin11
--with-gxx-include-dir=/include/c++/4.2.1
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5666) (dot 3)
$ clang -v
Apple clang version 2.1 (tags/Apple/clang-163.7.1) (based on LLVM 3.0svn)
Target: x86_64-apple-darwin11.1.0
Thread model: posix
EDIT: the 'multi-architecture' hypothesis a few have suggested sounded good, but I'm not sure it's right. If I force a single architecture with -arch, I get two warnings. If I specify -arch x86_64 -arch i386, I get two sets of duplicate warnings!
$ gcc-4.2 -Wall -arch x86_64 test.c
test.c: In function ‘main’:
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
$ gcc-4.2 -Wall -arch x86_64 -arch i386 test.c
test.c: In function ‘main’:
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
test.c: In function ‘main’:
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
test.c:6: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
EDIT: I don't get dupes for all warning types. -Wformat is the only one I've come across so far. For example, if I throw in an unused variable I only get one warning for that:
$ cat test.c
#include <stdio.h>
int main (int argc, char const *argv[])
{
long foo = 0l;
long bar;
printf("%i\n", foo);
return 0;
}
$ gcc-4.2 -Wall test.c
test.c: In function ‘main’:
test.c:7: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
test.c:7: warning: format ‘%i’ expects type ‘int’, but argument 2 has type ‘long int’
test.c:6: warning: unused variable ‘bar’
This is because Apple's stdio.h header attaches a GCC format attribute to its declaration of printf()...
(e.g. see the declaration of printf() here and the declaration of the __printflike() macro here)
...but GCC (and Clang, due to it trying to be very GCC-compatible!) already has built-in knowledge that printf() is a function which takes printf-style arguments. You're getting one warning due to the built-in knowledge, and a second warning due to the explicit attribute.
You can demonstrate the same behaviour on other platforms (with at least several versions of GCC) by doing the same thing yourself:
extern int printf(const char *, ...) __attribute__((__format__ (__printf__, 1, 2)));
int main (int argc, char const *argv[])
{
long foo = 0l;
printf("%i\n", foo);
return 0;
}
If you're targetting two CPU architectures (ARMv6/ARMv7 on iOS, for instance, or i386/x86_64 on Mac), you'll see two copies of each warning because the compiler runs twice for each file (once for each architecture.)
On a Mac, you can get it up to 4 warnings per line if you enable PPC/PPC64 support. ;)
Edit: Matthew's got it spot-on in the accepted answer.
It looks like you're compiling for iOS. The code is being compiled multiple times for multiple architectures. The warning is being generating for each architecture.

Isn't const supposed to be constant?

Check out this code
#include<stdio.h>
int main()
{
const int a=7;
int *p=&a;
(*p)++;
printf("*p=%d\np=%u\na=%d\n&a%u",*p,p,a,&a);
getch();
}
The output you get for this is
*p=8
p=1245064
a=8
&a1245064
How is this possible?? We declared variable a as constant. Doesnt this mean that the location pointed to by a can never be changed during the course of pgm execution??
That's undefined behavior - in your case it is working as you described, but it could just as well crash the program or cause any other problems. In your case const doesn't prevent the compiler from allocating the variable in modifiable memory, so you technically can modify it by obtaining a pointer to that variable and working through the pointer.
Undefined behaviour is not to be relied upon ;-)
Would you really want to use C if it actually prevented that from working? The fact it works that way is very much in the spirit of the language.
Reading your comment "it only gives a warning saying suspicious pointer conversion" it should be clear enough to deduce that you are doing something illegal.
You are assigning to a int * variable a const int * value.
The fact that C hasn't any runtime checkings to prevent you from modifying that memory address doesn't imply that it's allowed! (and in fact, the static type system checking is telling you that).
If yours doesn't detect this automatically, just get yourself a decent compiler. E.g clang gives me 4 problems with your code:
clang -c -o test-const.o test-const.c
test-const.c:17:7: warning: initializing 'int const *' discards qualifiers, expected 'int *' [-pedantic]
int *p=&a;
^ ~~
test-const.c:19:20: warning: conversion specifies type 'unsigned int' but the argument has type 'int *' [-Wformat]
printf("*p=%d\np=%u\na=%d\n&a%u",*p,p,a,&a);
~^ ~
test-const.c:19:32: warning: conversion specifies type 'unsigned int' but the argument has type 'int const *' [-Wformat]
printf("*p=%d\np=%u\na=%d\n&a%u",*p,p,a,&a);
~^ ~~
test-const.c:20:2: warning: implicit declaration of function 'getch' is invalid in C99 [-Wimplicit-function-declaration]
getch();
^
4 diagnostics generated.
These are all serious problems.

Resources