2009-07-20 16 views
9

Nella questione Why should we typedef a struct so often in C?, rilassarsi risposto che:Come posso nascondere la dichiarazione di una struttura in C?

In quest'ultimo caso, non è possibile tornare il Point di valore, dal momento che la sua dichiarazione è nascosta da parte degli utenti del file di intestazione. Questa è una tecnica ampiamente utilizzata in GTK +, ad esempio.

Come si nasconde la dichiarazione? Perché non posso restituire il punteggio in base al valore?

ADD:

ho capito il motivo per cui non può restituire le struct per valore, ma, è ancora difficile da capire perché non posso deferenza questo punto della mia funzione. Ad esempio, se la mia struttura ha un membro chiamato y, perché non posso farlo?

pointer_to_struct->y = some_value;

Perché dovrei usare i metodi per farlo? (Come Gtk +)

Grazie ragazzi, e scusa ancora per il mio cattivo inglese.

risposta

26

Dai un'occhiata a questo esempio di una libreria, utilizzando un file di intestazione pubblico, un file di intestazione privato e un file di implementazione.

Nel file di public.h:

struct Point; 

Point* getSomePoint(); 

Nel file di private.h:

struct Point 
{ 
    int x; 
    int y; 
} 

Nel file di private.c:

Point* getSomePoint() 
{ 
    /* ... */ 
} 

Se si combinano questi tre file in una libreria, si dà solo public.h e il file dell'oggetto della libreria al consumatore della libreria.

getSomePoint deve restituire un puntatore a Point, perché public.h non definisce la dimensione del Point, solo che è una struttura e che esiste. I consumatori della biblioteca possono utilizzare i puntatori a Point, ma non possono accedere ai membri o copiarli, perché non conoscono le dimensioni della struttura.

quanto riguarda la tua ulteriore domanda: Non si può risolvere il riferimento perché il programma che utilizza la libreria non avere solo le informazioni da private.h, che non contiene le dichiarazioni membri. Pertanto non può accedere ai membri della struttura puntiforme.

Puoi vedere questo come la funzione di incapsulamento di C, proprio come dichiareresti i membri dei dati di una classe C++ come privati.

5

Ciò che intende è che non è possibile restituire il valore di intere della struct nell'intestazione, perché per quello, la struttura deve essere dichiarata completamente. Ma ciò accade nel file C (la dichiarazione che rende X un tipo completo è "nascosto" nel file C e non esposto nell'intestazione), nel suo esempio.Quanto segue dichiara solo un tipo incompleto, se questa è la prima dichiarazione della struct

struct X; 

Quindi, è possibile dichiarare la funzione

struct X f(void); 

Ma non si può definire la funzione, perché non è possibile creare una variabile di quel tipo, e tanto meno di restituirlo (la sua dimensione non è nota).

struct X f(void) { // <- error here 
    // ... 
} 

L'errore si verifica perché "x" è ancora incompleto. Ora, se si include solo l'intestazione con la dichiarazione incompleta in esso, non è possibile chiamare tale funzione, poiché l'espressione della chiamata di funzione produrrebbe un tipo incompleto, che è vietato accadere.

Se si dovesse presentare una dichiarazione di tipo completo struct X in mezzo, sarebbe valida

struct X; 
struct X f(void); 

// ... 
struct X { int data; }; 
struct X f(void) { // valid now: struct X is a complete type 
    // ... 
} 

Questo varrebbe per il modo in cui utilizza typedef troppo: Entrambi nome stesso, (anche incompleta) genere. Una volta utilizzando un identificatore ordinario X e un'altra volta utilizzando un tag struct X.

5

Nel file di intestazione:

typedef struct _point * Point; 

Dopo il compilatore vede questo lo sa:

  • c'è s struct chiamato _point
  • c'è un tipo di puntatore Point che può fare riferimento a un _point

Il compilatore non sa:

  • ciò che la struct _point assomiglia
  • ciò che i membri contiene
  • quanto è grande

E non solo il compilatore lo sa - noi, come i programmatori non lo sanno neanche . Questo significa che non possiamo scrivere codice che dipende da quelle proprietà di _point, il che significa che il nostro codice potrebbe essere più portabile.

dato il codice di cui sopra, è possibile scrivere funzioni come:

Point f() { 
    .... 
} 

perché Point è un puntatore e puntatori sono tutti della stessa dimensione & il compilatore non ha bisogno di sapere altro su di loro. Ma non si può scrivere una funzione che restituisce per valore:

_point f() { 
    .... 
} 

perché il compilatore non sa nulla di _point, in particolare la sua dimensione, di cui ha bisogno per costruire il valore di ritorno.

Quindi, possiamo solo fare riferimento a _point tramite il tipo Point, che in realtà è un puntatore. Questo è il motivo per cui Standard C ha tipi come FILE, a cui è possibile accedere solo tramite un puntatore: non è possibile creare un'istanza di struttura FILE nel codice.

3

Che cosa significa che il post è: Se si vede l'intestazione

typedef struct _Point Point; 

Point * point_new(int x, int y); 

allora non si conoscono i dettagli di implementazione di Point.

1

Come alternativa all'utilizzo di puntatori opachi (come altri hanno detto), si può invece tornare un sacchetto opaca di byte, se si vuole evitare l'uso di memoria heap:

// In public.h: 
struct Point 
{ 
    uint8_t data[SIZEOF_POINT]; // make sure this size is correct! 
}; 
void MakePoint(struct Point *p); 

// In private.h: 
struct Point 
{ 
    int x, y, z; 
}; 

void MakePoint(struct Point *p); 

// In private.c: 
void MakePoint(struct Point *p) 
{ 
    p->x = 1; 
    p->y = 2; 
    p->z = 3; 
} 

Quindi, è possibile creare istanze della struttura nello stack nel codice client, ma il client non sa cosa contiene, tutto ciò che sa è che è un blob di byte con una determinata dimensione. Naturalmente, può ancora accedere ai dati se può indovinare gli offset e i tipi di dati dei membri, ma di nuovo si ha lo stesso problema con i puntatori opachi (anche se i client non conoscono la dimensione dell'oggetto in quel caso).

Ad esempio, i vari le strutture utilizzate nei pthreads uso struct biblioteca di byte opachi per i tipi come pthread_t, pthread_cond_t, ecc - è ancora possibile creare istanze di quelli sullo stack (e di solito lo fanno), ma si non ho idea di cosa ci sia in loro. Dai un'occhiata al tuo /usr/include/pthreads.h e ai vari file che include.

+1

Dovrai problema di allineamento, pubblico 'struct Point' allineamento nativo il requisito è basato sul tipo di dati uint8_t, ad es. 1 byte, mentre il requisito di allineamento nativo 'struct Point' privato è basato su' int', probabilmente 4 byte. Ho cercato di spiegare il problema nella risposta http://stackoverflow.com/a/17619016/611560 Cordiali saluti. – ydroneaud

2

vecchia domanda, risposta migliore:

nel file di intestazione:

typedef struct _Point Point; 

In C File:

struct _Point 
{ 
    int X; 
    int Y; 
}; 
Problemi correlati