Home > Software engineering >  Warning: implicit declaration of function 'calloc'
Warning: implicit declaration of function 'calloc'

Time:11-27

#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:

  1. Why doesn't the GCC give an error? Because as far as I understand, calloc is located at stdlib.h, but I didn't include it in my program. Why does my program still know what is calloc?

  2. 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:

  1. 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.

  2. 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.

  •  Tags:  
  • c
  • Related