I stumbled over an interesting question in a forum a long time ago and I want to know the answer.
Consider the following C function:
#include <stdbool.h> bool f1() { int var1 = 1000; int var2 = 2000; int var3 = var1 + var2; return (var3 == 0) ? true : false; }
This should always return false
since var3 == 3000
. The main
function looks like this:
#include <stdio.h> #include <stdbool.h> int main() { printf( f1() == true ? "true\n" : "false\n"); if( f1() ) { printf("executed\n"); } return 0; }
Since f1()
should always return false
, one would expect the program to print only one false to the screen. But after compiling and running it, executed is also displayed:
$ gcc main.c f1.c -o test $ ./test false executed
Why is that? Does this code have some sort of undefined behavior?
Note: I compiled it with gcc (Ubuntu 4.9.2-10ubuntu13) 4.9.2
.
C has no concept of boolean other than 0 being false, and anything else being true. The idea of returning 0 comes from having a return code indicating what the failure is. If you're not doing a return code, then there's nothing wrong with treating 1 and 0 as true and false.
You aren't passing the length parameter to get length, and even if you did, you are passing length by value so it won't be changed. Pass by reference.
Zero is used to represent false, and One is used to represent true. For interpretation, Zero is interpreted as false and anything non-zero is interpreted as true. To make life easier, C Programmers typically define the terms "true" and "false" to have values 1 and 0 respectively.
If you do not specify a return type or parameter type, C will implicitly declare it as int .
As noted in other answers, the problem is that you use gcc
with no compiler options set. If you do this, it defaults to what is called "gnu90", which is a non-standard implementation of the old, withdrawn C90 standard from 1990.
In the old C90 standard there was a major flaw in the C language: if you didn't declare a prototype before using a function, it would default to int func ()
(where ( )
means "accept any parameter"). This changes the calling convention of the function func
, but it doesn't change the actual function definition. Since the size of bool
and int
are different, your code invokes undefined behavior when the function is called.
This dangerous nonsense behavior was fixed in the year 1999, with the release of the C99 standard. Implicit function declarations were banned.
Unfortunately, GCC up to version 5.x.x still uses the old C standard by default. There is probably no reason why you should want to compile your code as anything but standard C. So you have to explicitly tell GCC that it should compile your code as modern C code, instead of some 25+ years old, non-standard GNU crap.
Fix the problem by always compiling your program as:
gcc -std=c11 -pedantic-errors -Wall -Wextra
-std=c11
tells it to make a half-hearted attempt to compile according the (current) C standard (informally known as C11).-pedantic-errors
tells it to whole-heartedly do the above, and give compiler errors when you write incorrect code which violates the C standard.-Wall
means give me some extra warnings that might be good to have.-Wextra
means give me some other extra warnings that might be good to have.You don't have a prototype declared for f1()
in main.c, so it is implicitly defined as int f1()
, meaning it is a function that takes an unknown number of arguments and returns an int
.
If int
and bool
are of different sizes, this will result in undefined behavior. For example, on my machine, int
is 4 bytes and bool
is one byte. Since the function is defined to return bool
, it puts one byte on the stack when it returns. However, since it's implicitly declared to return int
from main.c, the calling function will try to read 4 bytes from the stack.
The default compilers options in gcc won't tell you that it's doing this. But if you compile with -Wall -Wextra
, you'll get this:
main.c: In function ‘main’: main.c:6: warning: implicit declaration of function ‘f1’
To fix this, add a declaration for f1
in main.c, before main
:
bool f1(void);
Note that the argument list is explicitly set to void
, which tells the compiler the function takes no arguments, as opposed to an empty parameter list which means an unknown number of arguments. The definition f1
in f1.c should also be changed to reflect this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With