Una funzione esterna può (in C) essere dichiarata localmente, all'interno di qualche funzione, o anche qualche blocco; per es.
void myfunction(int x) {
extern void exit(int);
if (x==42)
exit(23);
Quindi non hai bisogno di alcuna intestazione #include
-d per dichiarare qualche funzione esterna - e più tardi chiamarla -, ma è molto più conveniente. Quindi non sono necessarie intestazioni per chiamare qualsiasi funzione esterna, solo una dichiarazione extern
di esso (e prima di questo, qualsiasi struct
, union
, typedef
, enum
... dichiarazioni relative ai tipi necessari per quello funzioni esterne).
L'esempio precedente utilizza la funzione standard exit
come esempio, ma potresti fare qualcosa in più, ad es. dichiara una funzione struct
e una extern
restituendola, come
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);
}
Inoltre, un compilatore C considererebbe qualsiasi funzione non dichiarata (ma chiamata) come restituendo un int
. Dovresti chiedere al tuo compilatore di emettere un avviso in quel caso. Se utilizzi GCC invocalo con un comando che inizia con gcc -Wall -Wextra -g
per ottenere tutti gli avvisi ( -Wall
), alcuni extra di questi ( -Wextra
) e informazioni di debug ( -g
). Quindi usa il debugger ( gdb
).
Dovresti leggere ulteriori informazioni sui linker (ad es. il libro di Levine su Linkers & Loaders ). Generalmente, non si preoccupano dei tipi o delle firme, ma solo dei nomi (quindi delle lingue con overloading , come C ++ , sono implementati con compilatori che eseguono alcuni nome mangling ).
Finalmente, alcuni sistemi hanno linker dinamici e sono in grado di caricare plugin in fase di esecuzione. Il mio sistema Linux ha (come tutti i sistemi POSIX) dlopen & dlsym e nel mio MELT strumento Sto facendo cose simili a:
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 realtà, dovresti aggiungere molti più controlli di errore.
Tutte le stringhe letterali superiori a "foobar.c"
, "foobarfun"
.... potrebbero essere calcolate in fase di esecuzione!
Una funzione può essere chiamata in C indirettamente attraverso un puntatore alla funzione. Un'implementazione potrebbe fornire funzioni (come dlsym
) in grado di restituire puntatori ad altre funzioni.
Quindi vedi che su Linux potresti emettere qualche codice C in fase di runtime, compilarlo, quindi accedere e invocare una funzione il cui nome è noto solo in fase di esecuzione in quel codice ....
I plugin e il loro caricamento dinamico con dlopen
non sono definiti dallo standard di linguaggio C ma da POSIX. Alcuni altri sistemi operativi (ad esempio Windows, che non conosco) hanno anche capacità di caricamento dinamiche.
Si noti che lo standard C non conosce le librerie , solo per circa unità di traduzione (o unità di compilazione).