In C, come sono accessibili le funzioni se non sono in linea o chiamate da #include? [chiuso]

0

Sto guardando il codice che in qualche modo chiama una libreria, ma non vedo la chiamata a nessuna delle intestazioni della libreria in qualsiasi momento. Ho fatto ricerche all'interno del codice e non vedo da nessuna parte le funzioni, typedef o le strutture che vengono definite ovunque ... eppure compila e funziona, e LDD mostra che chiama la libreria. Che cosa è inerente al linguaggio C che consente a questo di funzionare?

Codice che fa riferimento a funzioni / typedefs / structs inesistenti altrove nel codice:

typedef struct pngbuffer_s
{
    byte *buffer;
    png_size_t size, offset;
} pngBuffer_t;
    
posta Volumetricsteve 10.07.2015 - 19:29
fonte

2 risposte

2

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

    
risposta data 10.07.2015 - 21:56
fonte
3

C presume che le funzioni non dichiarate prendano un numero arbitrario di argomenti e restituiscano un int. Errori e typedef non definiti dovrebbero sempre causare un errore. La mia ipotesi sarebbe che uno dei file che si include includa la direttiva di inclusione necessaria per questa libreria. La maggior parte dei compilatori supporta un'opzione per stampare le intestazioni che includono durante la compilazione.

    
risposta data 10.07.2015 - 20:04
fonte

Leggi altre domande sui tag