2016-01-22 7 views
8

consideri il seguente programma di esempio:Conversione di un puntatore a una struttura per il suo primo membro

#include <stdio.h> 

struct base { 
    int a, b; 
}; 

struct embedded { 
    struct base base; 
    int c, d; 
}; 

struct pointed { 
    struct base* base; 
    int c, d; 
}; 

static void base_print(struct base* x) { 
    printf("a: %d, b: %d\n", x->a, x->b); 
} 

static void tobase_embedded(void* object) { 
    base_print(object); // no cast needed, suitably converted into first member. 
} 

static void tobase_pointed(void* object) { 
    struct base* x = *(struct base**) object; // need this cast? 
    base_print(x); 
} 

int main(void) { 
    struct embedded em = {{4, 2}}; 
    struct pointed pt = {&em.base}; 
    tobase_embedded(&em); 
    tobase_pointed(&pt); 
    return 0; 
} 

compilato con:

$ gcc -std=c99 -O2 -Wall -Werror -pedantic -o main main.c 

Il risultato atteso è:

$ ./main 
a: 4, b: 2 
a: 4, b: 2 

La C99 lo standard dice questo sul primo membro di una struttura:

C99 6.7.2.1 (13): Un puntatore a un oggetto struttura, opportunamente convertito, punta al suo membro iniziale ... e viceversa. Ci può essere padding senza nome dentro come oggetto struttura, ma non all'inizio.

Nel programma di esempio, un puntatore a struct embedded viene convertito in un puntatore a struct base (attraverso void*) senza la necessità di un cast esplicito.

E se invece il primo membro è un puntatore alla base come in struct pointed? Non sono sicuro del cast entro il tobase_pointed. Senza il cast, viene stampato il garbage, ma nessun avviso/errore di compilazione. Con il cast vengono stampati i valori corretti per base.a e base.b, ma ciò non significa molto se si verifica un comportamento non definito.

Il cast per convertire struct pointed nel suo primo membro struct base* è corretto?

+0

Il primo membro di 'struct pointed' è un puntatore a' struct base'. Ciò richiederà il dereferenziamento dell'oggetto void *. Ma non è possibile dereferenziare 'void *' senza dire al compilatore come dereferenziare il puntatore, quindi una necessità di cast. – alvits

risposta

2

Il codice non esegue solo il cast, ma denota anche il puntatore al puntatore a struct base. Questo è necessario per ottenere il puntatore alla base in primo luogo.

Questo è ciò che accade nel codice, se la funzione tobase_pointed stata rimossa:

struct pointed pt = {&em.base}; 
void* object = &pt;     //pass to the function 
struct base** bs = object;   //the cast in the function 
assert(bs == (struct base**)&pt) ; //bs points to the struct pointed 
assert(bs == &(pt.base)) ;   //bs also points to the initial member struct base* base 
struct base* b = *bs ;    //the dereference in the function 
base_print(x); 

bs è il puntatore che è stato opportunamente convertita per puntare all'elemento iniziale. Il tuo codice è corretto

+1

Vedendolo passo dopo passo lo rende cristallino, grazie. – Adam

1

Questo cast è giustificato e necessario perché si desidera convertire un puntatore in un puntatore al puntatore. Se non lanci, il dereferenziamento sarà errato.

In altre parole, lo base* ha lo stesso indirizzo dell'oggetto pt. Quindi puoi accedervi tramite un puntatore a pt. Ma devi dereferenziarlo.

Problemi correlati