An external function can (in C) be declared locally, inside some function, or even some block; e.g.
void myfunction(int x) {
extern void exit(int);
if (x==42)
exit(23);
So you don't need any #include
-d header to declare some external function - and later call it -, but that is a lot more convenient. Hence no headers are needed to call any external function, just an extern
declaration of it (and before that, any struct
, union
, typedef
, enum
... declarations related to types needed for that external functions).
The example above is using the standard exit
function as an example, but you could do something more, e.g. declare a struct
and an extern
function returning it, like
if (x>0) {
struct pair_st { int aa, bb; };
extern struct pair_st foobar(int, int);
struct pair_st p = foobar(x+2, x-3);
exit (p.aa+p.bb);
}
In addition, a C compiler would consider any undeclared (but called) function as returning an int
. You should ask your compiler to issue a warning in that case. If using GCC invoke it with a command starting with gcc -Wall -Wextra -g
to get all warnings (-Wall
), some extra of them (-Wextra
), and debugging info (-g
). Then use the debugger (gdb
).
You should read more about linkers (e.g. Levine's book on Linkers & Loaders). Generally, they don't care about types or signatures, but just about names (therefore, languages with overloading, like C++, are implemented with compilers doing some name mangling).
At last, some systems have dynamic linkers and are able to load plugins at runtime. My Linux system has (as all POSIX systems) dlopen & dlsym, and in my MELT tool I am doing things similar to:
FILE* fgensrc = fopen("foobar.c", "w");
emit_some_c_code(fgensrc); // a complex function emiting some C code at runtime
fclose (fgensrc);
// now, compile the runtime generated C file foobar.c into a shared object
if (system("gcc -fPIC -Wall -O -shared foobar.c -o ./foobar.so")>0)
exit(EXIT_FAILURE);
// open the shared object as a plugin
void* dlh = dlopen("./foobar.so", RTLD_NOW|RTLD_GLOBAL);
// declare a signature typedef
typedef int myintsig_t (int, int);
// declare a pointer to a function of that signature
myintsig_t* ptrfun =
// fetch a function pointer by its name at runtime!
(myintsig_t*) dlsym(dlh,"foobarfun");
if (!ptrfun) { fputs(stderr, dlerror()); exit(EXIT_FAILURE); };
/// call the function thru the pointer!
x = (*ptrfun) (x, x+3);
In reality, you should add a lot more of error checks.
All the literal strings above "foobar.c"
, "foobarfun"
.... could be computed at runtime!
A function can be called in C indirectly thru a pointer to function. An implementation could provide functions (like dlsym
) able to return pointers to other functions.
So you see that on Linux you could emit some C code at runtime, compile it, then access and invoke a function whose name is only known at runtime in that code....
Plugins and their dynamic loading with dlopen
is not defined by the C language standard but by POSIX. Some other operating systems (e.g. Windows, which I don't know) also have such dynamic loading abilities.
Notice that the C standard does not know about libraries, only about translation units (or compilation units).