implicit declaration of function in C: causes and fix
Since GCC 14, calling an undeclared function is an error. Missing include, missing prototype, typo in the name: the cause and the fix for each case.
The same file compiled last year, or on a classmate's machine, with at worst a yellow warning you let scroll past. Now your GCC rejects it outright: error: implicit declaration of function. GCC has simply stopped guessing on your behalf.
In short
implicit declaration of function 'f' means you call f at a point where the compiler has not yet seen any declaration of that function. Since GCC 14, this is an error by default in C99 and later modes, and so it is with GCC 15 too. Three causes cover almost every case: the header that declares the function is not included (<stdio.h> for printf, <string.h> for strdup, <unistd.h> for sleep or fork), the function is defined below main with no prototype above it, or its name is misspelled. Fix the declaration, not the compiler flags.
What does "implicit declaration of function" mean?
The C compiler reads your file from top to bottom. When it reaches a call, it must already know the function: its return type and the type of each of its parameters. That description is called a declaration, and when it spells out the parameters, a prototype. Without one, the compiler knows neither how to pass the arguments nor what to do with the value that comes back.
The shortest case there is, a printf without #include:
int main(void){ printf("Hello\n"); return 0;}$ gcc -Wall -Wextra hello.c -o hellohello.c: In function 'main':hello.c:3:5: error: implicit declaration of function 'printf' [-Wimplicit-function-declaration] 3 | printf("Hello\n"); | ^~~~~~hello.c:1:1: note: include '<stdio.h>' or provide a declaration of 'printf' +++ |+#include <stdio.h> 1 | int main(void)hello.c:3:5: warning: incompatible implicit declaration of built-in function 'printf' [-Wbuiltin-declaration-mismatch] 3 | printf("Hello\n"); | ^~~~~~hello.c:3:5: note: include '<stdio.h>' or provide a declaration of 'printf'The useful line is the note: GCC even suggests the line to add, prefixed with +++. The warning after it, incompatible implicit declaration of built-in function, comes from the fact that GCC knows printf out of the box: it can tell that the declaration it would have invented does not match the real one. Add #include <stdio.h> at the top of the file and all four messages go away together.
Why did this code compile before?
Because the standard changed in 1999 and GCC took twenty-five years to draw every consequence. In C89, calling an unknown function was allowed: the compiler acted as if you had written extern int f();, a function that returns an int and whose arguments go unchecked. C99 removed this implicit declaration, but GCC kept accepting it with a mere warning up to GCC 13. GCC 14, released on May 7, 2024, made it an error, and GCC 15 keeps it that way.
| Mode or version | Calling an undeclared function |
|---|---|
-std=gnu89 | accepted, without a word unless you enable -Wall |
| GCC 5 to 13, C99 and later modes | accepted with a warning |
| GCC 14 and 15 | rejected: error, no executable produced |
GCC 14 and 15 with -fpermissive | accepted with a warning |
So the old behavior still exists, behind a flag. With -fpermissive, the same hello.c compiles and runs:
$ gcc -fpermissive hello.c -o hello && ./hellohello.c: In function 'main':hello.c:3:5: warning: implicit declaration of function 'printf' [-Wimplicit-function-declaration] 3 | printf("Hello\n"); | ^~~~~~hello.c:1:1: note: include '<stdio.h>' or provide a declaration of 'printf' +++ |+#include <stdio.h> 1 | int main(void)hello.c:3:5: warning: incompatible implicit declaration of built-in function 'printf' [-Wbuiltin-declaration-mismatch] 3 | printf("Hello\n"); | ^~~~~~hello.c:3:5: note: include '<stdio.h>' or provide a declaration of 'printf'HelloHere it happens to work, because printf really does return an int. The next section shows why the GCC maintainers finally closed the door.
Why implicit calls are dangerous
A function declared implicitly is assumed to return an int. If it returns something else, a pointer for example, the caller only gets the part that fits in an int. On a 64-bit machine, an int is 32 bits and a pointer is 64: half the address gets lost on the way.
Here are two files. name.c defines a function that returns a pointer, and main.c calls it without declaring it:
#include <stdio.h>#include <stdlib.h>#include <string.h>
char *make_name(void){ char *name = malloc(1 << 20);
strcpy(name, "grademe"); printf("make_name returns %p\n", (void *)name); return name;}#include <stdio.h>
int main(void){ char *name = make_name();
printf("main receives %p\n", (void *)name); printf("%s\n", name); return 0;}GCC 15 refuses to compile it, with two errors: the implicit declaration, then the conversion from int to pointer that this declaration brings with it. Forcing the old behavior with -fpermissive shows what those errors prevent:
$ gcc -Wall -Wextra main.c name.c -o namemain.c: In function 'main':main.c:5:18: error: implicit declaration of function 'make_name' [-Wimplicit-function-declaration] 5 | char *name = make_name(); | ^~~~~~~~~main.c:5:18: error: initialization of 'char *' from 'int' makes pointer from integer without a cast [-Wint-conversion]$ gcc -fpermissive main.c name.c -o namemain.c: In function 'main':main.c:5:18: warning: implicit declaration of function 'make_name' [-Wimplicit-function-declaration] 5 | char *name = make_name(); | ^~~~~~~~~main.c:5:18: warning: initialization of 'char *' from 'int' makes pointer from integer without a cast [-Wint-conversion]$ ./namemake_name returns 0xffffb5ebf010main receives 0xffffffffb5ebf010Segmentation faultCompare the two addresses. The low 32 bits (b5ebf010) survived, and the high ones were replaced with fs: main received a negative int, which it sign-extended into a pointer. That address designates nothing, and the first access crashes. The crash happens in the last printf, but the fault lies upstream: main.c never declares make_name. This is exactly the kind of error GCC 14 chose to stop at compile time.
Arguments fare no better. A function that expects two doubles, called before its definition, would receive two ints that nobody converts:
#include <stdio.h>
int main(void){ printf("%.1f\n", average(12, 15)); return 0;}
double average(double a, double b){ return (a + b) / 2;}GCC notices the conflict between what it assumed at line 5 and what it finds at line 9:
$ gcc -Wall -Wextra average2.c -o average2...average2.c:9:8: error: conflicting types for 'average'; have 'double(double, double)' 9 | double average(double a, double b) | ^~~~~~~average2.c:5:22: note: previous implicit declaration of 'average' with type 'int()' 5 | printf("%.1f\n", average(12, 15)); | ^~~~~~~with type 'int()': there is the declaration the compiler had invented, spelled out in full.
Cause 1: the header is not included
That is the printf case above. Every C library function is declared in a specific header, and forgetting it only shows up when you compile.
$ gcc -Wall -Wextra copy.c -o copycopy.c: In function 'main':copy.c:5:18: error: implicit declaration of function 'strdup' [-Wimplicit-function-declaration] 5 | char *copy = strdup("grademe"); | ^~~~~~copy.c:5:18: warning: incompatible implicit declaration of built-in function 'strdup' [-Wbuiltin-declaration-mismatch]$ gcc -Wall -Wextra pause.c -o pausepause.c: In function 'main':pause.c:6:5: error: implicit declaration of function 'sleep' [-Wimplicit-function-declaration] 6 | sleep(1); | ^~~~~This time, GCC suggests no header. To find it, open the man page: the answer is in its SYNOPSIS section. The number is the manual section: 3 for library functions, 2 for system calls (the man(1) entry explains how the manual is split).
$ man 3 strdup | head -20strdup(3) Library Functions Manual strdup(3)
NAME strdup, strndup, strdupa, strndupa - duplicate a string
LIBRARY Standard C library (libc, -lc)
SYNOPSIS #include <string.h>
char *strdup(const char *s);
char *strndup(const char s[.n], size_t n); char *strdupa(const char *s); char *strndupa(const char s[.n], size_t n);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
strdup():The headers for the functions people forget most often, taken from the man pages:
| Function | Header | Page to open |
|---|---|---|
printf, scanf, fgets | <stdio.h> | man 3 printf |
malloc, free, atoi, exit | <stdlib.h> | man 3 malloc |
strlen, strdup, strcmp, memcpy | <string.h> | man 3 strlen |
write, fork, sleep | <unistd.h> | man 2 write |
waitpid | <sys/wait.h> | man 2 waitpid |
isalpha, toupper | <ctype.h> | man 3 isalpha |
sqrt, pow | <math.h>, plus -lm at link time | man 3 sqrt |
The last row hides a second trap: once <math.h> is included, compilation succeeds, but linking can still fail with undefined reference to `sqrt'. That is a different stage and a different error, explained in undefined reference to in C.
Cause 2: the function is defined after main
You did write your function, but below main. At the point of the call, the compiler has not read it yet.
#include <stdio.h>
int main(void){ printf("%d\n", average(12, 15)); return 0;}
int average(int a, int b){ return (a + b) / 2;}$ gcc -Wall -Wextra average.c -o averageaverage.c: In function 'main':average.c:5:20: error: implicit declaration of function 'average' [-Wimplicit-function-declaration] 5 | printf("%d\n", average(12, 15)); | ^~~~~~~Two fixes work: move the definition above main, or leave the definition where it is and announce the function at the top of the file with its prototype, which is the first line of the definition followed by a semicolon.
#include <stdio.h>
int main(void){ printf("%d\n", average(12, 15)); return 0;}#include <stdio.h>
int average(int a, int b);
int main(void){ printf("%d\n", average(12, 15)); return 0;}$ gcc -Wall -Wextra average_ok.c -o average && ./average13As soon as the program spans several files, the prototypes move to a .h header that each file includes. It is the same mechanism as <stdio.h>, at your scale.
fix-the-prototype
report.c does not compile: main calls print_report, which is defined further down. Read the diagnostic, then fix the file with a prototype or by moving the definition, without changing a single byte of the three printed lines.
Cause 3: a typo in the name
The compiler has no idea that prinft is a typo: to it, this is one more unknown function. GCC does compare the name with the ones it knows, though, and suggests the closest.
$ gcc -Wall -Wextra typo.c -o typotypo.c: In function 'main':typo.c:5:5: error: implicit declaration of function 'prinft'; did you mean 'printf'? [-Wimplicit-function-declaration] 5 | prinft("Hello\n"); | ^~~~~~ | printfWhen did you mean shows up, read it first. With -fpermissive, which reproduces the behavior of older versions, the typo gets through compilation and only surfaces at link time, with a much less helpful message:
$ gcc -fpermissive typo.c -o typotypo.c: In function 'main':typo.c:5:5: warning: implicit declaration of function 'prinft'; did you mean 'printf'? [-Wimplicit-function-declaration] 5 | prinft("Hello\n"); | ^~~~~~ | printf/usr/bin/ld: /tmp/cclpSAcq.o: in function `main':typo.c:(.text+0x10): undefined reference to `prinft'collect2: error: ld returned 1 exit statusFrequently asked questions
I included <string.h> and still get the error on strdup. Why?
You are probably compiling with -std=c99 or -std=c17. In those strict modes, glibc only declares what the C standard provides, and strdup is a POSIX function that only entered the C standard with C23. GCC then answers implicit declaration of function 'strdup'; did you mean 'strcmp'? despite the include. Add #define _POSIX_C_SOURCE 200809L before any #include, as the "Feature Test Macro Requirements" section of man 3 strdup says, or compile with -std=gnu17 or in C23.
What does "incompatible implicit declaration of built-in function" mean?
GCC knows the signature of many standard functions in advance, such as printf(3), strdup(3) or malloc. When you call one of them without its header, it notices that the int f() declaration it would have assumed does not match the real one. The cure is the same: include the header.
Why "implicit declaration of function 'itoa'" when I included <stdlib.h>?
itoa is part of neither the C standard nor POSIX, and glibc does not provide it: man 3 itoa answers No manual entry. To convert an integer to text, use snprintf(text, sizeof text, "%d", n), or write your own version as in int_to_str.
Do I need a prototype for every function?
Yes, for every function called before its definition or defined in another file. A function defined higher up in the same file is its own declaration. In practice, the prototypes of shared functions go in a .h, and small internal functions, declared static, keep the "definition before call" order.
Sources
GCC 14: Porting to GCC 14, new default errors including implicit-function-declaration
GCC 14.1: -Wimplicit-function-declaration, "enabled by default, as an error, in C99 and later"
GCC 13.2: -Wimplicit-function-declaration, still a plain warning
cppreference: implicit declaration as
extern int f();until C99
