Come formattare un puntatore a funzione?

C’è un modo per stampare un puntatore a una funzione in ANSI C? Naturalmente questo significa che dovete lanciare la funzione di puntatore a puntatore a void, ma sembra che non è possibile??

#include <stdio.h>

int main() {
    int (*funcptr)() = main;

    printf("%p\n", (void* )funcptr);
    printf("%p\n", (void* )main);

    return 0;
}

$ gcc -ansi -pedantic -Parete di prova.c-o
prova
test.c: In function
‘main’:
test.c:6: avviso: ISO C
proibisce la conversione di puntatore a funzione
per oggetto il tipo di puntatore
test.c:7:
avvertenza: ISO C vieta di conversione di
la funzione di puntatore a puntatore a oggetto
tipo
$ ./prova
0x400518
0x400518

È “lavoro”, ma non di serie…

  • Bene, stavo per accettare una risposta che ha funzionato, fino a quando lo ha cancellato (anche se non aveva senso).
  • Si può typecast di un int (o int 64-bit su sistemi di grandi dimensioni) e di stampa che, invece?
  • Dorgan: il cast di un puntatore ad un intero di tipo diverso intptr_t o uintptr_t è definito dall’implementazione, tuttavia l’implementazione di questi tipi è facoltativo secondo lo standard.
  • Mi dispiace per l’eliminazione, la mia risposta, non ero sicuro se sarebbe soddisfare le vostre flag di compilazione così ho temporaneamente rimosso solo in caso di non.
  • Quello che stavo cercando era un modo legale per farlo, e la tua sembra legale, e il compilatore chiude troppo, quindi grazie 🙂
  • lungo tempo non ho usato la C, ma è sempre più esoterico, che cosa è accaduto in linguaggio C, ora? Giuseppe Quinsey osservato che quando questo flag -O2 -Wstrict-aliasing è utilizzato, dreamlax suggerimento avrà anche un avvertimento
  • Buen: conosco la sensazione. Spesso mi suggeriscono di codice che sembra funzionare e ottenere martellato con commenti su come viola sezione bla bla. Ho letto ora le spec quasi ogni volta che rispondo a una C-domanda correlata.
  • Vorrei uomo può permettersi di spec…
  • Perché, c’è un gran numero di cose che sono nelle categorie di “Non è consentito, ma è supportato da molte implementazioni come estensione” e “Non garantiti per funzionare, ma sembra funzionare sulla maggior parte delle implementazioni più comuni” – così la vostra esperienza e l’intuizione può ingannare.
  • Utilizzare questo PDF, open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf. È il C99 spec con le correzioni, quindi bisogna essere un po ‘ attenti, ma è raro per C89 e C99 essere leggermente diverse (codice viene compilato con diverso comportamento). Normalmente sono palesemente diversi, quindi se si tenta di utilizzare qualcosa da C99, che non è in C89, GCC con -pedantic non compilarlo.
  • Jessop, grazie per il consiglio, avevo paura a usare il C99 spec prima per C89, ma sembra che l’unico mezzo decente opzione, in realtà io probabilmente solo uso C99 per ora comunque.
  • wow, come se qualcuno -1 sarebbe questo…

 

6 Replies
  1. 44

    L’unico modo legale per farlo è quello di accedere al byte che compongono il puntatore del mouse utilizzando un tipo di carattere. Come questo:

    #include <stdio.h>
    
    int main() {
        int (*funcptr)() = main;
        unsigned char *p = (unsigned char *)&funcptr;
        size_t i;
    
        for (i = 0; i < sizeof funcptr; i++)
        {
            printf("%02x ", p[i]);
        }
        putchar('\n');
    
        return 0;
    }

    Giocando il puntatore a funzione come un void *, o non tipo di carattere, come dreamlax la risposta, è definito il comportamento.

    Che quei byte che compongono il puntatore a funzione, in realtà media è dipendente dall’implementazione. Potrebbero rappresentare un indice in una tabella di funzioni, per esempio.

    • Non sarebbe meglio usare uintptr_t invece di unsigned char ? Come in int (*funcptr)() = main; uintptr_t *p = (uintptr_t *)&funcptr; printf("0x%" PRIxPTR "\n", *p);, evitando un ciclo? (Supponendo che C99 stdint.h e inttypes.h intestazioni sono supportate).
    • non ha alcuna relazione con i puntatori a funzione, ed è facoltativo. Così dal POV di scrivere qualcosa di totalmente portatile no, non va meglio.
    • caf risposta è corretta. E ‘ carino vedere dreamlax è definito codice accettato come risposta, mentre il codice corretto, si trova qui.
    • programmatore: la Mia risposta è stata accettata prima della caf ha aggiunto la sua risposta.
    • Sì beh, questo non sarà bella come %p 🙁 per Non parlare di endianess e non corretta rappresentazione segmentato modelli.
    • printf con nessun argomento viene automaticamente convertito in putchar su gcc btw 🙂 anche con-O0.
    • È vero che endianness è un problema in modo che questa risposta non è perfetto. D’altra parte, dove lo standard definisce %p per i puntatori a dati, non richiede implementazioni di essere ragionevole circa endianness 🙂
    • Purtroppo, fino ad un printf identificatore di formato per la stampa di puntatori a funzione, è il migliore che si può fare rigorosamente conformi C. Endianness è un red herring – non c’è alcuna garanzia che un “puntatore a funzione” è un qualsiasi tipo di numero (per esempio, potrebbe essere il primo di 64 caratteri del nome della funzione, che è contenuta nella tabella dei simboli quando viene effettuata una chiamata utilizzando la funzione di puntatore). Questo è il motivo per cui i puntatori a funzione, non sono convertibili e da altri tipi di puntatore – è così che l’attuazione è ampia latitudine di come li implementa.
    • il caf è di destra, no puntatore è definito per essere qualsiasi tipo di numero, tranne che, ovviamente, è fatta di bit, in modo che possa essere interpretata come un numero, che è che cosa fa questo codice. È anche quello che il cast a int fa normalmente per i puntatori a oggetti, ma non garantite. Naturalmente, in un modello di memoria flat a sarebbe leggermente irritante per la stampa di indirizzi byte meno significativo è il primo, ma se hai mai usato una memoria debugger a livello di un le di piattaforma, si sa che si può abituarsi a questo.
    • Bene con le normali puntatori a oggetti, è possibile aggiungere il numero di un puntatore e ottenere un altro puntatore, quindi ci deve essere di almeno qualche componente numerica per loro. Puntatori a funzione, non sono però – le uniche cose che si possono fare con il loro valore è di confrontarli con altri puntatori a funzione, confrontarli con NULL, o chiamare la funzione a cui punta.
    • per me,un altro molto scuro punto di C std.Perché dovrebbe funz ptrs essere trattata come un caso particolare non è chiaro;gli stessi problemi potrebbero essere dati “puntatori”, ad esempio,int c;, in grado di assicurare &c è l’effettivo indirizzo di memoria dove il dato è,piuttosto che un interno, l’attuazione definite “cosa”?A partire forse dal std dice così, come al solito, ok. Ma allora perché non std forza implementazioni di dare la “giusta”, intendendo per tale operazione, come la stampa l’indirizzo di una funzione, dove, naturalmente, non siamo interessati a ispezionare l’,ma per conoscere una realtà (il punto di ingresso del codice vero e proprio)
    • IIRC, sizeof non applicato sui tipi di funzione. Quindi dubito sizeof funcptr è legale.
    • non può essere applicata in funzione i designatori, ma funcptr non è una funzione di designazione, è un puntatore a una funzione e applicazione di sizeof è perfettamente legale.
    • “Giocando il puntatore a funzione come un void *, o non tipo di carattere […] è un comportamento indefinito.” Perché giocando a char* è valido, mentre per void* non è? Entrambi i puntatori hanno la stessa rappresentazione e i requisiti di allineamento. Inoltre, come da Appendice A. 6.2, quando “Un puntatore a una funzione è convertito in un puntatore a un oggetto o a un puntatore a un oggetto viene convertito in un puntatore a una funzione (3.3.4)”, UB si verifica. Sei sicuro che sia valido per fare ciò che hai fatto nella risposta?
    • Da “giocando il puntatore a funzione come un void * voglio dire, la lettura del byte di un puntatore a funzione come se fosse una void * oggetto. char * è altrettanto valido – che non è un tipo di personaggio sia. Costruito come un tipo di carattere sarebbe la lettura del byte di puntatore a funzione come chars o unsigned chars.
    • Il codice emette un avvertimento: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]. Suggerisco di cambiare int i; per size_t i; per risolvere il problema.
    • Certo, ho aggiornato. Il vecchio non era in realtà un problema – i non potrebbe mai essere negativo, quindi il problema di promozione-per-unsigned non si pone (e il compilatore dovrebbe essere in grado di capire che, vedendo che i inizia da zero e sono sempre e solo incrementato).

  2. 5

    C’è l’uso dei sindacati che può muoversi di avviso/errore, ma il risultato è ancora (più probabile) un comportamento indefinito:

    #include <stdio.h>
    
    int
    main (void)
    {
      union
      {
        int (*funcptr) (void);
        void *objptr;
      } u;
      u.funcptr = main;
    
      printf ("%p\n", u.objptr);
    
      return 0;
    }

    Si possono confrontare due puntatori a funzione (ad es. printf ("%i\n", (main == funcptr));) utilizzando un’istruzione if per verificare se sono uguali o no (so che compromette completamente la scopo e potrebbe benissimo essere irrilevante), ma per quanto riguarda in realtà l’output l’indirizzo del puntatore a funzione, ciò che accade è per il fornitore della piattaforma di destinazione libreria C e il compilatore.

    • Tipo-giocando con i sindacati non è undefined comportamento. È definito dall’implementazione comportamento, e la maggior parte dei compilatori fa esattamente quello che ci si aspetta.
  3. 2

    Lanciare la funzione di puntatore ad un intero, quindi il cast a un puntatore di nuovo per l’uso “%p”.

    #include <stdio.h>
    
    int main() {
        int (*funcptr)() = main;
    
        printf("%p\n", (void *)(size_t) funcptr);
        printf("%p\n", (void *)(size_t) main);
    
        return 0;
    }

    Notare che su alcune piattaforme (ad esempio DOS a 16 bit nel “medio” o “compact” modelli di memoria), puntatori a dati e puntatori a funzioni non sono della stessa dimensione.

    • Sì, “DOS a 16 bit” correva su una memoria segmentata architettura. Per esempio, l ‘ 80286 sarebbe un 4 byte void * e 2 byte size_t.
  4. 1

    Provare questo:

    #include <stdio.h>
    #include <inttypes.h>
    
    
    int main() {
        int (*funcptr)() = main;
        unsigned char *p = (unsigned char *)&funcptr;
        int i;
    
        /* sample output: 00000000004005e0 */
        printf("%016"PRIxPTR"\n", (uintptr_t)main);
        /* sample output: 00000000004005e0 */
        printf("%016"PRIxPTR"\n", (uintptr_t)funcptr);
    
        /* reflects the fact that this program is running on little-endian machine
        sample output: e0 05 40 00 00 00 00 00 */
        for (i = 0; i < sizeof funcptr; i++)
        {
            printf("%02x ", p[i]);
        }
        putchar('\n');
    
        return 0;
    }

    Usato questo flag:

    gcc -ansi -pedantic -Wall -O2 -Wstrict-aliasing c.c

    Nessun avvisi emessi utilizzando quelle bandiere

    • Su una macchina in cui più di byte necessari per memorizzare un puntatore a una funzione di un puntatore a un UINT, le prime due printf volontà di uscita troncato informazioni. Per ottenere gcc per mettervi in guardia su calchi che potenzialmente tronca (anche se forse non la vostra macchina), forse hai bisogno di una bandiera per mettere in guardia sui potenziali troncamenti, piuttosto che su alias.
    • Penso che sia più severe bandiera che può essere passato a un compilatore C. Domani sarò download 32 bit di Ubuntu e verificare se il codice verrà eseguito. Mi metterà alla prova anche in Visual C++ 2008 se avrebbe fatto lo stesso
    • “Su una macchina in cui più di byte necessari per memorizzare un puntatore a una funzione di un puntatore a un UINT” <– hmm.. (sans la memoria segmentata di pre-386, dove il compilatore ha tre sapore di puntatori: vicino, lontano, enorme(enorme è poco più di un compilatore magia di vera architettura di macchina)) mi ricordo che in linguaggio assembly, non c’è di dimensione variabile puntatore, indipendentemente da dove si punti a (BOOLEAN, char, short, funzione, etc)
    • Un’architettura Harvard potrebbe avere diverse dimensioni di istruzioni e dati di puntatori, e per questo che C li tratta come incompatibile. Io non riesco a mano il nome di un processore che ha dimensioni diverse (è molto più facile in C++, in cui puntatore-a-membro di solito è un diverso e spesso di dimensioni variabili), ma anche io non riesco a concludere la mia ignoranza che non ci sono più, e la domanda è “in ANSI C”, non “in ANSI C su x86”.
    • hmm.. ho letto en.wikipedia.org/wiki/Modified_Harvard_architecture. Non è una macchina di Von Neumann; si dice che C non è stato progettato per Hardvard Architettura, in modo che l’attuazione del linguaggio C su che macchina sarà sicuramente molto non standard, controllare la nota a piè di pagina. In sostanza, non si riesce a fare in C un programma che può essere eseguito su tutte le architetture di computer. Nell’architettura di Von Neumann, la memoria può contenere istruzioni e dati, così da avere solo uno spazio di indirizzi, quindi stessa dimensione del puntatore, il runtime può fare cose come l’applicazione delle patch di un proprio ” codice in fase di esecuzione e in esecuzione runtime generato istruzioni
    • Credo che per altri esoterico di architetture di non Von Neumann, che ha di speciale printf(“%p”) per indirizzo di istruzione(14-bit di indirizzo) rispetto al suo indirizzo dati(8 bit); dire “%ip”. O il programmatore di avere a che fare con la dispersione #ifdefs. Anche io non riesco a riempire la mia mente in quel 14 bit di indirizzo, come possiamo implementare looping dimensione in byte, in modo che l’istruzione di indirizzo possono essere stampati. Fare C implementazioni su quelle architetture difficilmente conforme agli standard ANSI C
    • Non so la storia del successo C implementazioni su architetture Harvard (e diversi “Harvard” architetture di un aspetto diverso in ogni caso, quindi forse è fattibile su alcuni, ma non in generale). Non credo che il tuo ciclo necessariamente pone problemi su un architettura Harvard: è funcptr che stai trattando come tipo di dati char, non il codice di main, e funcptr è una variabile automatica e così è dati anche su un architettura Harvard. Ho appena controversia che la tua osservazione “mi ricordo che in linguaggio assembly, non c’è di dimensione variabile puntatore” è sufficiente a concludere nulla di legale, portatile, C.
    • La cosa che C standard espressamente impedisce di fare è (a) supponendo che i dati e il codice puntatori sono uguali, e (b) la conversione di un codice puntatore a un puntatore a dati o altrimenti fare qualcosa che potrebbe consentire di affrontare il codice macchina stessa. Un puntatore un codice puntatore a un puntatore a dati, in modo che la vostra conversione per char* bene, indipendentemente dal codice di puntatori a guardare come. Devi solo fare attenzione che i puntatori a funzione, può essere legalmente un formato diverso da void* in un conforme implementazione C (su una architettura di von Neumann o altro). Non c’è alcuna garanzia uinptr_t detiene una funzione ptr.
    • Btw, che l’articolo collegato nella nota a piè di pagina di Wikipedia parla della difficoltà di memorizzazione dei dati nel programma spazio in C implementazione di una architettura Harvard. Da quanto ho capito, questo non significa che non è possibile implementare C, poiché i dati in codice di spazio non è richiesto per implementare C. È solo cattive notizie se la sola lettura di dati statici, i valori letterali stringa, e così via, deve occupare RAM di valore per essere utilizzato come dati da C. so C è potenzialmente inefficiente con separata di indirizzamento. C è principalmente “progettato per” von Neumann architetture, ma AFAIK non garantisce uno.
    • (uintptr_t)main è definito il comportamento (o per dirla in altro modo, si basa sul compilatore estensioni non indicato da Standard C)

  5. 1

    Con gcc 4.3.4, utilizzando interruttori -O2 -Wstrict-aliasing, dreamlax la risposta di produrre:

    warning: dereferencing type-punned pointer will break strict-aliasing rules

    Aggiunto: penso che le obiezioni del caf risposta circa endianness e le dimensioni sono ragionevoli, che la sua soluzione non è l’indirizzo (no pun intended). Dustin suggerimento per l’utilizzo di una unione cast è probabilmente legale (anche se da quello che ho letto sembra che ci sia un po ‘ di dibattito, ma tuo compilatore non è importante quanto la legge). Ma il suo codice potrebbe essere semplificato (o offuscato, a seconda del vostro gusto) da una fodera:

    printf("%p\n", ((union {int (*from)(void); void *to;})funcptr).to);

    Questo rimuove il gcc severi-aliasing avviso (ma è corretto?).

    Di aggregazione cast non di “lavoro”, se si utilizza il pedante interruttore, o utilizzando, ad esempio, SGI IRIX, quindi è necessario utilizzare:

    printf("%p\n", ((union {int (*from)(void); void *to;} *)&funcptr)->to);

    Ma per quanto riguarda la domanda iniziale: la sua origine è nell’uso di pedante, che credo sia leggermente pedante :).

    Ulteriore modifica: Nota non è possibile utilizzare principale nell’ultimo esempio, come in:

    printf("%p\n", ((union {int (*from)(void); void *to;})   main).to);  //ok
    printf("%p\n", ((union {int (*from)(void); void *to;} *)&main)->to); //wrong!

    perché, naturalmente, &principali decade principale.

    • Informazioni utili, ma questo non rispondere alla sua domanda, deve essere stato un commento su la mia risposta in contrapposizione a una risposta separato del tutto.
    • Mi dispiace per la risposta separato, ma io ho solo 31 stackoverflow punti, a meno di 50 necessari per i commenti. Ma io a convertire questa risposta a un commento al più presto.
    • Mi dispiace. Vedo che hai già fatto questo punto dreamlax risposta la tua risposta.
  6. 0

    Non sono sicuro se questo è kosher, ma si ottiene l’effetto senza un loop, i sindacati, i cast non di tipo puntatore, o dipendenze extra oltre string.h

    int (*funcptr)() = main;
    void* p = NULL;
    memcpy(&p, (void**) &funcptr, sizeof(funcptr));
    printf("%p", p);

    Nessun avvisi con gcc -ansi -pedantic -Wall -Wstrict-aliasing su GCC 7.1.1

Lascia un commento