In both cases (void m
with caller expecting int
, void main
with caller-in-C-library expecting int
), this is undefined behavior, and your compiler should have chewed you out, e.g. [GCC 4.8 with -Wall]:
test.c:3:6: warning: return type of ‘main’ is not ‘int’
void main()
^
test.c: In function ‘main’:
test.c:7:5: warning: implicit declaration of function ‘m’
int k = m();
^
test.c: At top level:
test.c:13:6: warning: conflicting types for ‘m’
void m()
^
test.c:7:13: note: previous implicit declaration of ‘m’ was here
int k = m();
^
"Implicit declarations" exist only for backward compatibility with pre-C1989 programs, which are more than 20 years out of date at this point. If you had written this code in a more modern style you would've gotten a hard error:
#include <stdio.h>
extern void m(void);
int main(void)
{
int k = m();
printf("%d", k);
return 0;
}
void m(void)
{
printf("hello");
}
⇒
test.c: In function ‘main’:
test.c:7:13: error: void value not ignored as it ought to be
int k = m();
^
The key addition is the extern void m(void);
line. The compiler isn't allowed to look ahead to the definition of m
when it checks main
for errors, so you need a forward declaration to tell it what m
's type really is. You should also know that in C (but not C++), void m()
means "m
takes an unspecified number of arguments". To declare a function taking no arguments, which is what you wanted, you have to write void m(void)
.
I believe what is happening on your specific system is that the return value of printf
, which your code officially ignores, is being left in the return-value register and thus interpreted as the return value of m
, but you cannot rely on anything predictable happening. That is what 'undefined behavior' means.