#include<stdio.h>
int *arr
int main()
{
arr = calloc(1, sizeof(int));
free(arr);
return 0;
}
As far as I understand, this warning occurs because I did not declare the function in header (In this case I should've included stdlib.h
). My questions are:
Why doesn't the GCC give an error? Because as far as I understand,
calloc
is located atstdlib.h
, but I didn't include it in my program. Why does my program still know what iscalloc
?Should we close our eyes at the warning? Because my program works well even without include of
stdlib.h
.
CodePudding user response:
As far as I understand, this warning occurs because I did not declare the function in header (In this case I should've included
<stdlib.h>
).
You are correct. The compiler complains that you are calling a function for which it has not seen a declaration. Including <stdlib.h>
does provide a proper declaration for the function calloc
. Note that you could also have provided a declaration yourself by adding the line: void *calloc(size_t, size_t);
. Yet it is recommended to use the standard include files to get the exact declarations for library functions.
Why doesn't the GCC give an error?
Because the compiler is lenient in order to compile older programs that were written before the C Standard was even published. It used to not be an error to call functions without a declaration or a definition in scope. The compiler would just infer the prototype from the types of the arguments provided. In your case the prototype is inferred as int calloc(int, size_t)
, which is obviously incorrect and will cause undefined behavior. To avoid such problems, the compiler issues a warning about calloc
being undeclared.
Why does my program still know what is
calloc
?
It does not, the prototype is inferred from the call statement and this guess falls short. As a matter of fact, the compiler should also complain about the int
return value being implicitly converted to an int *
when stored to arr
. If int *
and int
have a different representation on the target system (as is the case on 64-bit systems), the value of arr
will be invalid and free(arr)
will definitely have undefined behavior too.
The compiler produces an executable because the function calloc
is found in the C library, which is linked to all C programs implicitly.
Should we close our eyes at the warning?
Definitely not. You should compile your program with all warnings enabled using gcc -Wall -Wextra -Werror
, and gcc will consider all warnings like fatal errors and refuse to produce an executable until they are corrected. This will save you many hours of debugging time. It is a pity this behavior is not the default one.
my program works well even without include of
<stdlib.h>
.
Well it appears to work on your system, but it fails on mine (assuming I remove my default compiler options that prevent gcc
from making an executable). The program has undefined behavior. Do not rely on chance.
Here is a proper version:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = calloc(1, sizeof(*arr)); /* use object type for consistency */
printf("pointer value is %p\n", (void *)arr);
free(arr);
return 0;
}
CodePudding user response:
The function is not located at
stdlib.h
, it is in the same shared library object,libc.so
so your linker has no problem finding the symbol. If you tried to call a made up function, you also wouldn't get a compiler error, rather your linker would complain about being unable to resolve a symbol.You technically can, but you absolutely shouldn't. If you wouldn't you wouldn't be warned about argument errors. The Linker does not (and can not) check, if you supplied the right amount and types of arguments.
Example:
// a.c
int asdf(int b){
return b 1;
}
// main.c
int main(void) {
asdf();
return 0;
}
If you compile now: gcc main.c a.c
You will only get a warning, while if you had included a header you would get an error. This is BAD, since it can lead to undefined behavior that can lead to unexpected crashes, memory corruption, security issues, etc.
You should always give your compiler a fair opportunity to help you.
EDIT: Clarify, that ignoring is bad
CodePudding user response:
calloc
is not in stdlib.h
. Nothing is in stdlib.h
. Nothing is in any .h
(or nothing should).
What is in stdlib.h
is just (about calloc
I mean)
extern void *calloc(size_t nmemb, size_t size);
calloc
is in the libc
. That is where your program will find it when it will call it.
Strictly speaking, you don't need anything else (I mean, from an execution point of view) to be able to call a function.
All .c
codes are compiled to do their task. libc
has been compiled long ago. And at linking time (for static libraries, and for dynamic to point to them) and then at run time, all functions from all compiled code are loaded. So your main
from your code and calloc
will find each other at that time.
The problem is not there.
The problem is that in order to compile your main, the compiler need to know how calloc
is supposed to be called.
It needs to know it to check for syntax error. Otherwise you could pass 3 arguments, or only one, to calloc
, and the compiler would have no way to know that this is not correct.
You could pass arguments of the wrong type, and likewise.
It needs to know also to know how many bytes it is supposed to push as an argument, and even how it is supposed to pass arguments.
See for example these two codes
one.c
#include <stdio.h>
void printInt(int a, int b){
printf("ints %d %d\n", a, b);
}
void printFloat(float a, float b){
printf("floats %f %f\n", a, b);
}
void printDouble(double a, double b){
printf("doubles %f %f\n", a, b);
}
two.c
#include <stdio.h>
int main(void) {
printInt(1,2);
printInt(1.0, 2.0);
printFloat(1,2);
printFloat(1.0,2.0);
printDouble(1,2);
printDouble(1.0,2.0);
}
Compile them with (depending on your compiler)
gcc -std=gnu99 -o main one.c two.c # There is an implicit -lc here including libc, that contains printf
On my computer it prints
ints 1 2
ints 1034078176 -2098396512
floats 0.000000 0.000000
floats 0.000000 0.000000
doubles 0.000000 0.000000
doubles 1.000000 2.000000
See that it works as intended only for printInt
called with ints, and printDouble
called with doubles. It fails to cast 1.0
as an int to call printInt
, or on the contrary to cast 1
as a double to call printDouble
. And For printFloat
it fails in all cases, because the compiler assumed wrongly the size of the arguments to be pushed.
But other than that, those 3 functions are called. It is not the the code of the function that is missing. It is the ability of the compiler, when calling them, to call them correctly.
Just add, in two.c
the declarations
extern void printInt(int, int);
extern void printFloat(float, float);
extern void printDouble(double, double);
(Or create a one.h
containing those, and #include "one.h"
in two.c
, it leads to the same result)
And now the output is as expected
ints 1 2
ints 1 2
floats 1.000000 2.000000
floats 1.000000 2.000000
doubles 1.000000 2.000000
doubles 1.000000 2.000000
And I haven't even started with types declarations.
#include
are not meant to provide libraries, and the functions that are in them. That you do while linking, adding .o
and -lsomelib
to the liking command line (or using other way, depending on your compiler).
#include
are there to provide codeless declarations that the compiler needs to know how to call those functions.