2013-01-03 19 views
11

Mentre seguivo alcuni tutorial e leggendo i puntatori di funzione ho appreso che evidentemente assegnando un puntatore vuoto a un puntatore di funzione in ISO C non è definito, esiste un modo per risolvere l'avviso che ricevo durante la compilazione (ad esempio un modo migliore per codificarlo) o dovrei semplicemente ignorarlo?ISO C Void * e Function Pointers

Attenzione:

ISO C forbids assignment between function pointer and 'void *' [-pedantic] 

esempio di codice:

void *(*funcPtr)(); 
funcPtr = GetPointer(); 

GetPointer è una funzione che restituisce un puntatore nullo AD ESEMPIO

void *GetPointer(); 
+3

Che ne dici di non assegnare un puntatore nullo a una funzione puntatore? –

+4

In teoria non è definito, poiché alcune macchine avevano dimensioni diverse per indirizzi di codice e indirizzi di dati. In pratica, nella maggior parte delle architetture oggi, gli indirizzi di codice e dati sono della stessa dimensione, nello stesso spazio degli indirizzi. –

+0

Cosa restituisce GetPointer()? È quella seconda linea in cui ottieni l'errore? –

risposta

6

No. Il compilatore è giusto, e anche voi: in C89 e C99, non è possibile convertire tra i puntatori di dati (che void * è) e puntatori a funzione, in modo che l'unico modo per risolvere l'avvertimento sta tornando un puntatore di funzione dalla funzione.

(Si noti, tuttavia, che, in pratica, questo funziona nonostante l'avvertimento, e anche lì è questa incongruenza nella libreria standard - la funzione dlsym() viene utilizzato per ottenere puntatori a funzione, ma restituisce void * - Quindi, in sostanza si può ignorare l'avviso Funzionerà, anche se in senso stretto il comportamento non è definito qui.)

+0

In effetti il ​​codice funziona Mi interessava solo studiare gli stili di codifica migliori possibili (quindi perché codifico con avvertimenti così rigorosi), è meno sul non avere avvertimenti e altro sulla comprensione delle loro cause, effetti e modi per evitarli. –

+0

@Tomwaivory Sì, capisco, ed è sicuramente una buona pratica. In questo caso, è qualcosa che non va nella lingua :) Quindi non preoccuparti, la pratica non è la stessa teoria, a meno che tu non stia lavorando su un DS9k, funzionerà con questo UB all'interno, e puoi andare a battere i membri del comitato standard C con un bastone: D –

+0

Grazie, è bello sapere, non riesco a sistemare la lingua, faccio del mio meglio per codificare intorno ad esso :) –

2

Ho riscontrato questo problema utilizzando glib. Le strutture dati di Glib, come GSList, di solito hanno un campo chiamato dati void *. Volevo funzioni memorizzare in una lista e ottenuto un sacco di errori di simile a questo:

warning: ISO C forbids passing argument 2 of ‘g_slist_append’ between function pointer and ‘void *’ [-pedantic] 

Questo esempio genera una serie di avvertimenti usando gcc -Wall -ansi -pedantic

typedef int (* func) (int); 

int mult2(int x) 
{ 
    return x + x; 
} 

int main(int argc, char *argv[]) 
{ 
    GSList *functions = NULL; 
    func f; 

    functions = g_slist_append(functions, mult2); 
    f = (func *) functions->data; 
    printf("%d\n", f(10)); 
    return 0; 
} 

Così ho avvolto la funzione in una struct e tutti gli avvertimenti vanno via:

struct funcstruct { 
    int (* func) (int); 
}; 

int mult2(int x) 
{ 
    return x + x; 
} 

int main(int argc, char *argv[]) 
{ 
    GSList *functions = NULL; 
    struct funcstruct p; 
    p.func = mult2; 

    functions = g_slist_append(functions, &p); 
    p = * (struct funcstruct *) functions->data; 
    printf("%d\n", p.func(10)); 
    return 0; 
} 

e 'discutibile che questo è un bel po' di codice aggiuntivo per rendere alcuni avvisi scompaiono, ma non mi piace il mio codice per generare avvisi. Inoltre, quanto sopra sono esempi di giocattoli. Nel codice reale che sto scrivendo, risulta piuttosto utile racchiudere l'elenco delle funzioni in una struttura.

Sarei interessato a sapere se questo è problematico o se c'è un modo migliore di farlo.

+0

Se lo fai in questo modo, non puoi usare l'elenco al di fuori dell'ambito di 'p', perché il puntatore a' p' è valido solo nell'ambito di 'p'. Quindi la tua lista sarebbe abbastanza inutile. E rendere il tipo di struttura non era necessario. Se tu avessi semplicemente dichiarato 'p' come' int (* p) (int); 'funzionerebbe in modo abbastanza identico a quello che hai ora. – newacct

5

In tlpi-book ho trovato questo trucco molto interessante:

#include <dlfcn.h> 

int 
main(int argc, char *argv[]) 
{ 
    ... 
    void (*funcp)(void);  /* Pointer to function with no arguments */ 
    ... 
    *(void **) (&funcp) = dlsym(libHandle, argv[2]); 
} 
+0

Qualcuno potrebbe spiegare come funziona in maggiori dettagli? –

+1

@MatthieuPoullet: Sul lato destro, abbiamo un puntatore vuoto ('void *'). Sul lato sinistro, prendiamo l'indirizzo di un puntatore a funzione. Ignorando per un momento il '(void **)', il puntatore a un puntatore di funzione viene dereferenziato dal '' 'più a sinistra. Il '(void **)' sta solo dicendo che il puntatore a un puntatore di funzione dovrebbe essere lanciato su un puntatore a un puntatore void, il quale, quando dereferenziato dal '*' più a sinistra è quindi un 'void *'. Quindi, entrambi i lati sinistro e destro del compito hanno tipo "void *" e il compilatore è felice. – mtk

0

in base alla risposta @WeinanLi, ma utilizzando una funzione ausiliaria per chiarezza:

#include <dlfcn.h> 

/* Pointer to function with no arguments */ 
typedef void (functor_t*)(void); 

void load_symbol(functor_t* functor, void* dl_handle, const char* symbol_name) { 
    *(void**)functor = dlsym(dl_handle, symbol_name); 
} 

int 
main(int argc, char *argv[]) 
{ 
    // [...] 
    functor_t funcp; 
    // [...] 
    load_symbol(&funcp, libHandle, argv[2]); 
}