2012-09-15 15 views
5

Devo utilizzare il seguente blocco di codice per un incarico scolastico, SENZA ALCUN MODIFICA.Matrice dinamica di puntatori alle strutture

typedef struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
}* pStudentRecord; 

pStudentRecord* g_ppRecords; 
int g_numRecords =0; 

Qui g_ppRecords si suppone essere un array di puntatori a strutture. Quello che sto completamente non riuscendo a capire è che come può la dichiarazione pStudentRecords *g_ppRecords; significa g_ppRecords ad essere una matrice, poiché una matrice dovrebbe essere definito come

type arrayname[size]; 

Ho provato l'allocazione della memoria per g_ppRecords dinamicamente, ma non sta aiutando.

g_ppRecords = (pStudentRecord*) malloc(sizeof(pStudentRecord*)*(g_numRecords+1)); 
+0

Un puntatore punta semplicemente a un indirizzo. Puoi avere tutta la memoria riservata a partire da quell'indirizzo che desideri (fino a esaurimento spazio). – chris

+0

Penso che sia una domanda povera, capisci che un array può essere definito in questo modo, dato che provi ad allocare uno spazio per esso, quindi cosa stai chiedendo ?, non è sufficiente allocare uno spazio per il puntatore struct, che è btw pStudentRecord e non pSt ... ord *, per poterlo usare, è necessario anche assegnare un posto per la struct it self! – Michael

risposta

1

Osservare che pStudentRecord è typedef'd come puntatore a una struttura. I puntatori in C puntano semplicemente all'inizio di un blocco di memoria, indipendentemente dal fatto che quel blocco contenga 1 elemento (un normale puntatore "scalare") o 10 elementi (un puntatore "array"). Così, per esempio, il seguente

char c = 'x'; 
char *pc = &c; 

rende pc scegliere un pezzo di memoria che inizia con il carattere 'x', mentre il seguente

char *s = "abcd"; 

rende s scegliere un pezzo di memoria che inizia con "abcd" (seguito da un byte null). I tipi sono gli stessi, ma potrebbero essere utilizzati per scopi diversi.

Pertanto, una volta assegnato, ho potuto accedere agli elementi di g_ppRecords facendo ad es. g_ppRecords[1]->firstName.

Ora, di destinare questo array: si desidera utilizzare g_ppRecords = malloc(sizeof(pStudentRecord)*(g_numRecords+1)); (anche se nota che sizeof(pStudentRecord*) e sizeof(pStudentRecord) sono uguali poiché entrambi sono tipi di puntatore). Ciò rende una matrice non inizializzata di struttura puntatori. Per ogni puntatore della struttura nell'array, è necessario assegnargli un valore allocando una nuova struttura. Il nocciolo del problema è come si potrebbe allocare una singola struttura, vale a dire

g_ppRecords[1] = malloc(/* what goes here? */); 

Per fortuna, si può effettivamente dereference i puntatori in sizeof:

g_ppRecords[1] = malloc(sizeof(*g_ppRecords[1])); 

Nota che sizeof è un costrutto compilatore. Anche se g_ppRecords[1] non è un puntatore valido, il tipo è ancora valido e quindi il compilatore calcolerà la dimensione corretta.

+0

g_ppRecords [1] = (pStudentRecord *) malloc (sizeof (char *) * 2 + sizeof (int) + sizeof (float)); ? –

+0

Aggiunta una soluzione migliore. Non è in realtà una soluzione ovvia, vieni a pensarci. – nneonneo

+0

+1 per insegnarmi un paio di nuovi trucchi ... –

0

Una matrice viene spesso indicata con un puntatore al suo primo elemento. Se si dispone di spazio sufficiente per 10 record degli studenti e quindi si memorizza un puntatore all'inizio di tale spazio in g_ppRecords, g_ppRecords [9] conterà 9 lunghezze del puntatore del record in avanti e il dereferenziamento. Se hai gestito correttamente il tuo spazio, quale sarà l'ultimo record nell'array, perché hai riservato spazio sufficiente per 10.

In breve, hai assegnato lo spazio e puoi trattarlo come vuoi se è la lunghezza giusta, anche come array.

Non sono sicuro del motivo per cui stai allocando spazio per g_numRecords + 1 record. A meno che g_numRecords non venga chiamato in modo confuso, è lo spazio per un altro nell'array rispetto a quanto necessario.

-1

Qui g_ppRecords dovrebbe essere una matrice di puntatori alle strutture. Quello che non riesco a capire è che come può la dichiarazione * pStudentRecords g_ppRecords; significa che g_ppRecords è un array. come array deve essere definito come

tipo arrayname [dimensioni];

umm type arrayname[size]; è un modo di molti modi per definire una matrice in C.

questo definisce un array statico, con la maggior parte dei valori essendo memorizzati in pila a seconda del luogo dell'evento definizione, la dimensione dell'array deve essere nota al momento della compilazione, anche se questo potrebbe non essere più il caso in alcuni moderni compilatori.

un altro modo sarebbe creare dinamicamente un array in fase di esecuzione, quindi non dobbiamo conoscere la dimensione in fase di compilazione, questo è il punto in cui arrivano i puntatori, sono variabili che memorizzano l'indirizzo di blocchi di memoria allocati dinamicamente .

un semplice esempio potrebbe essere qualcosa di simile type *array = malloc(sizeof(type) * number_of_items); malloc restituisce un indirizzo di memoria che è memorizzato nella array, di notare che non typecast il tipo di ritorno per motivi di sicurezza.

Tornando al problema in questione.

typedef struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
}* pStudentRecord; 

pStudentRecord* g_ppRecords; 
int g_numRecords = 0; 

questo typedef è un po 'diverso dalla maggior notare il }* fondamentalmente è un puntatore a una struct quindi questo:

pStudentRecord* g_ppRecords; 

è effettivamente:

struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
}** pStudentRecord; 

è un puntatore a una puntatore, sul motivo per cui avrebbero definire il typedef in questo modo, è al di là di me, e io personalmente non lo consiglio, perché?

beh, un problema è come possiamo ottenere la dimensione della struttura attraverso il suo nome? Semplicemente non possiamo! se usiamo sizeof(pStudentRecord) otterremo 4 o 8 a seconda dell'architettura sottostante, perché questo è un puntatore, senza conoscere la dimensione della struttura non possiamo allocarlo dinamicamente usando il suo nome typedef, quindi cosa possiamo fare, dichiarare un secondo struct come questa:

typedef struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
} StudentRecord; 

g_ppRecords = malloc(sizeof(StudentRecord) * g_numRecords); 

in entrambi i casi si ha realmente bisogno di contattare la persona che ha creato questo originale codice o la gente mantenere e aumentare le vostre preoccupazioni.

g_ppRecords=(pStudentRecord) malloc((sizeof(char*) + 
            sizeof(char*) + 
            sizeof(int) + 
            sizeof(float)) *(g_numRecords+1)); 

questo può sembrare un modo possibile, purtroppo, ci sono no guarantees circa le strutture, in modo che possano effettivamente containg imbottitura tra i membri in modo che la dimensione totale della struct può essere in realtà più grande, allora i suoi membri combinati, non menzionare l'indirizzo probabilmente sarebbe diverso.

EDIT

A quanto pare siamo in grado di ottenere la dimensione della struttura, semplicemente dedurre il tipo

così:

pStudentRecord g_ppRecords = malloc(sizeof(*g_ppRecords) * g_numRecords); 

funziona bene!

+0

all'elettore in basso cura di elaborare? –

+0

Questo è un modo davvero pessimo di usare 'malloc' dato che si hardcode la definizione della struttura (e ignori padding, ecc.). Non hai ragione nel dire che non c'è altro modo di farlo; vedi la mia soluzione. Il codice come scritto è perfettamente valido e utilizzabile, anche se in qualche modo insolito. – nneonneo

+0

Ho detto ** modo più scadente **, comunque lo rimuoverò. –

3

MODIFICA: aggiornata la sezione "BIG MISTAKE".

Una lezione veloce su typedef in stile C (diverso da C++!), E perché è così com'è e come usarlo.

In primo luogo, un semplice trucco typedef.

typedef int* int_pointer; 
int_pointer ip1; 
int *ip2; 
int a; // Just a variable 
ip1 = &a; // Sets the pointer to a 
ip2 = &a; // Sets the pointer to a 
*ip1 = 4; // Sets a to 4 
*ip2 = 4; // Sets a to 4 

IP1 e IP2 sono dello stesso tipo: un puntatore-a-tipo-int, anche se non ha messo un * nella dichiarazione di IP1. Quello * era invece nella dichiarazione.

Argomenti di commutazione. Lei parla di array dichiarano come

int array1[4]; 

Per fare questo in modo dinamico in fase di esecuzione, si potrebbe fare:

int *array2 = malloc(sizeof(int) * 4); 
int a = 4; 
array1[0] = a; 
array2[0] = a; // The [] implicitly dereferences the pointer 

Ora, che cosa se vogliamo un array di puntatori? Sarebbe come questo:

int *array1[4]; 
int a; 
array1[0] = &a; // Sets array[0] to point to variable a 
*array1[0] = 4; // Sets a to 4 

Assegniamo quell'array dinamicamente.

int **array2 = malloc(sizeof(int *) * 4); 
array2[0] = &a; // [] implicitly dereferences 
*array2[0] = 4; // Sets a to 4 

Avviso l'int **. Ciò significa pointer-to pointer-to-int. Possiamo, se lo vogliamo, usare un puntatore typedef.

typedef int* array_of_ints; 
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4); 
array3[0] = &a; // [] implicitly dereferences 
*array3[0] = 4; // Sets a to 4 

Vedere come c'è solo uno * in quest'ultima dichiarazione? Questo perché uno di loro è "nel typedef". Con quest'ultima dichiarazione, ora disponi di una matrice di dimensione 4 che consiste di 4 puntatori a ints (int *).

È importante indicare PRECEDENZA OPERATORE qui. L'operatore di dereferenza [] ha la preferenza su *. Quindi, per essere assolutamente chiaro, quello che stiamo facendo è questo:

*(array3[0]) = 4; 

Ora, cambiamo argomenti da strutture e typedef.

struct foo { int a; }; // Declares a struct named foo 
typedef struct { int a; } bar; // Typedefs an "ANONYMOUS STRUCTURE" referred to by 'bar' 

Perché dovresti digitare una struttura anonima? Bene, per leggibilità!

struct foo a; // Declares a variable a of type struct foo 
bar b;  // Notice how you don't have to put 'struct' first 

Dichiarare una funzione ...

funca(struct foo* arg1, bar *arg2); 

Guarda come non abbiamo avuto bisogno di mettere 'struct' davanti arg2?

Ora, vediamo che il codice è necessario utilizzare definisce una struttura IN QUESTO MODO:

typedef struct { } * foo_pointers; 

Questo è analogo a come abbiamo fatto un array di puntatori prima:

typedef int* array_of_ints; 

Confronta side-by-side

typedef struct { } * foo_pointers; 
typedef int* array_of_ints; 

l'unica differenza è che uno è ad una struct {} e l'altro è in int.

Con i nostri foo_pointers, possiamo dichiarare un array di puntatori a foo come tale:

foo_pointers fooptrs[4]; 

Ora abbiamo una matrice che memorizza 4 puntatori ad una struttura anonima che non possiamo accedere.

TOPIC SWITCH!

PURTROPPO PER TE, il tuo insegnante ha commesso un errore. Se si guarda la dimensione di() del tipo foo_pointers sopra, si troverà che restituisce la dimensione di un puntatore a quella struttura, NON la dimensione della struttura. Questo è 4 byte per la piattaforma a 32 bit o 8 byte per la piattaforma a 64 bit. Questo perché abbiamo tipizzato un POINTER TO A STRUCT, non una struct stessa. sizeof (pStudentRecord) restituirà 4.

Quindi non è possibile allocare lo spazio per le strutture stesse in modo ovvio! Tuttavia, i compilatori tengono conto di questa stupidità. pStudentRecord non è un nome/tipo che puoi usare per allocare validamente memoria, è un puntatore a una struttura "concettuale" anonima, ma possiamo alimentare la dimensione di questo al compilatore.

pStudnetRecord g_ppRecords [2]; pStudentRecord * record = malloc (sizeof (* g_ppRecords [1]));

Una pratica meglio è quello di fare questo:

typedef struct { ... } StudentRecord; // Struct 
typedef StudentRecord* pStudentRecord; // Pointer-to struct 

Saremmo Ora abbiamo la possibilità di fare struct StudentRecord di, così come puntatori a loro con pStudentRecord di, in modo chiaro.

Anche se il metodo che si è obbligati a utilizzare è una pratica molto negativa, non è esattamente un problema al momento. Torniamo al nostro esempio semplificato usando gli interi.

E se voglio essere un typedef per complicare la mia vita ma spiegare il concetto che sta succedendo qui? Torniamo al vecchio codice int.

typedef int* array_of_ints; 
int *array1[4]; 
int **array2 = malloc(sizeof(int *) * 4); // Equivalent-ish to the line above 
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4); 
int a, b, c, d; 
*array1[0] = &a; *array1[1] = &b; *array1[2] = &c; *array1[3] = &d; 
*array2[0] = &a; *array2[1] = &b; *array2[2] = &c; *array2[3] = &d; 
*array3[0] = &a; *array3[1] = &b; *array3[2] = &c; *array3[3] = &d; 

Come potete vedere, siamo in grado di utilizzare questo con il nostro pStudentRecord:

pStudentRecord array1[4]; 
pStudentRecord *array2 = malloc(sizeof(pStudentRecord) * 4); 

Mettere tutto insieme, e ne consegue logicamente che:

array1[0]->firstName = "Christopher"; 
*array2[0]->firstName = "Christopher"; 

sono equivalenti. (Nota: non fare esattamente come ho fatto sopra, l'assegnazione di un puntatore char * in runtime a una stringa è OK solo se sai di avere già abbastanza spazio assegnato).

Questo mostra solo un ultimo bit.Cosa facciamo con tutto questo ricordo che abbiamo mallocato? Come lo liberiamo?

free(array1); 
free(array2); 

E c'è una alla fine di una lezione a tarda notte su puntatori, typedef di struct anonimi, e altre cose.

+0

Grazie per il tuo commento. Questo mi ha aiutato! – Rachael

Problemi correlati