2012-02-20 2 views
9

Penso di poter soffrire della temuta "malattia da programmatore accidentale", almeno quando si tratta di typedef e puntatori di funzione. Quindi ho sperimentato tutti i tipi di combinazioni che coinvolgono questi per analizzare i risultati sulla base di tutto l'output che ottengo.Come funziona typedef per i puntatori di funzioni

Ma mentre continuavo a provare diverse combinazioni, invece di analizzare i risultati, ora sono appena perso.

Spero che voi ragazzi mi aiutiate a capire questo casino.

primo esempio di codice

typedef void (print)(void); 
void do_something (void) { printf("Hello World\n"); } 

print *pr; 
pr = &do_something; 
pr(); // Hello World 

secondo codice di esempio

typedef void (print)(void); 
void do_something (void) { printf("Hello World\n"); } 

print *pr; 
pr = do_something; 
pr(); // Hello World 

Come fare sia il codice di cui sopra esempi funzionano, è come se '&' non ha alcun effetto sul nome della funzione

terzo codice esempio

typedef void (print)(void); 
void do_something (void) { printf("Hello World\n"); } 

print pr; 
pr = do_something; // compile error 
pr = &do_something; // compile error 
pr(); 

Speravo una delle assegnazioni di cui sopra per lavorare qui, ma accidenti! Io davvero non capisco i puntatori di funzione (e forse anche typedef).

+1

Questo può aiutare http://stackoverflow.com/questions/4298654/operator-optional-in-function-pointer-assignment –

+0

Umm ... Penso che dovresti farti un 'Expert C Programming' esplora C's var definire le regole. Cercherò di trovarlo online per te – shengy

+1

solo per la programmazione Expert C di Google, il primo è il libro. Cerca "Come si forma una dichiarazione" e penso che avrai la risposta – shengy

risposta

18

L'indirizzo di un nome di funzione e il nome di funzione semplice significano entrambi la stessa cosa, quindi & non ha alcun effetto sul nome di una funzione.

Allo stesso modo, quando si utilizza puntatori a funzione, dereferenziazione multipla non è un problema:

#include <stdio.h> 
typedef void print(void); 
static void dosomething(void) { printf("Hello World\n"); } 

int main(void) 
{ 
    print *f1 = dosomething; 
    print *f2 = &dosomething; 
    f2(); 
    (f1)(); 
    (*f1)(); 
    (**f2)(); 
    (***f1)(); 
    (****f2)(); 
    (*****f1)(); 
} 

che compila in modo pulito sotto:

gcc -O3 -g -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \ 
    -Wold-style-definition -std=c99 xx.c -o xx 

Non vorrei affermare che più stelle è buono stile; non lo è. È "strano e (si, si può dire) perverso".Uno è sufficiente (e l'unica è principalmente per persone come me che hanno imparato a programmare in C prima che lo standard dicesse "è corretto chiamare una funzione tramite un puntatore senza usare la notazione (*pointer_to_function)(arg1, arg2), puoi semplicemente scrivere pointer_to_function(arg1, arg2) se lo desideri"). Sì, è strano. No, nessun altro tipo (o classe di tipi) mostra lo stesso comportamento, grazie al cielo.

+1

Ora è strano e, posso dire, perverso. Per provare e compilare effettivamente. :) –

+4

+1, per chiarire, le funzioni e i puntatori di funzione non sono gli stessi, ma il primo tende a decadere automaticamente a quest'ultimo in molti contesti (anche se non tutti). Nell'espressione '** f2', il puntatore della funzione viene sottoposto a dereferenziazione e la funzione risultante si ritrasferisce immediatamente a un puntatore, che viene quindi nuovamente dereferenziato. – avakar

+2

Gli array hanno un comportamento simile, sebbene non identico, di "decadimento automatico". Per 'int x [10];', gli indirizzi di 'x' e' & x' sono gli stessi. Ma il loro tipo è diverso, e non puoi usare la bella torre stellata mostrata qui. – ugoren

3

Si scopre che, in C/C++, sia funcname e &funcname genererà l'indirizzo di funcname e può essere assegnato a una variabile di puntatore di funzione. Questo è in realtà solo una stranezza di come la sintassi è stata progettata per la lingua (s).

7

La cosa sui puntatori di funzione è che sono funzione di puntatori ! :-) Questo è il modo per ottenere il vostro terzo campione di lavorare:

#include <stdio.h> 
typedef void (*print)(void); 
//   ^
void do_something (void) { printf("Hello World\n"); } 
int main (void) { 
    print pr; 
    pr = do_something; // &do_something would also work. 
    pr(); 
    return 0; 
} 

In termini di se si utilizza funcName o &funcName, non importa (in C almeno). Sezione 6.3.2.1 Lvalues, arrays and function designators afferma:

Una designazione di funzione è un'espressione che ha un tipo di funzione. Tranne quando è l'operando dell'operatore sizeof o dell'operatore unario &, una designazione di funzione con tipo "function return type" viene convertita in un'espressione che ha tipo "pointer to function return type".

+0

pazzesca che il primo voto è stato un downvote: -/ –

3

Come C, C++ ha il puntatore alle funzioni: void (*)() per esempio è un puntatore a una funzione che non accetta argomenti e non restituisce alcun valore. Tuttavia, C++ ha anche introdotto riferimenti alle funzioni void (&)() e ci sono conversioni implicite tra i due (anche se non ricordo esattamente le regole).

Pertanto:

  • funcname è un riferimento operato
  • &funcname è un puntatore a funzione

Nota che prendendo l'indirizzo (o un riferimento a) una funzione che sovraccarica richiede a static_cast per il tipo esatto (per risolvere il sovraccarico).