2011-02-10 14 views
5
#include <stdio.h> 
#include <stdlib.h> 
typedef struct { 
    unsigned length; 
} List; 
void init(List *l) { 
    l = (List *) malloc(sizeof(List)); 
    l->length = 3; 
} 
int main(void) { 
    List *list = NULL; 
    init(list); 
    if(list != NULL) { 
     printf("length final %d \n", list->length); 
     return 0; 
    } 
    return 1; 
} 

Questa è una versione semplificata del codice che mi sta dando problemi. Sto cercando di costruire il puntatore *list da un metodo in cui è passatocome parametro.C - Impossibile avviare un puntatore passato come argomento

So che posso fare il lavoro void init(List *l) cambiandolo a void init(List **l) ma questo è per un tutorial di classe. Non riesco a cambiare gli argomenti del metodo. Ho trascorso quattro ore a lavorare su questo.

Voglio garantire che non ci sia modo di far funzionare void init(List *l) prima di affrontare il mio professore.

Grazie in anticipo

risposta

4

si sta passando una copia del puntatore a init, che è l'allocazione di memoria, riporlo nella sua copia locale, e prontamente perde quando init rendimenti. Non è possibile trasferire i dati alla funzione chiamante in questo modo. È necessario restituire i dati allocati o passare un puntatore al puntatore che si desidera modificare, entrambi richiedono la modifica della firma della funzione.

void init(List **l) { 
    *l = (List *) malloc(sizeof(List)); 
    (*l)->length = 3; 
} 

init(&list); 

Ha fatto l'assegnazione specificare che si deve allocare il List dall'interno init? In caso contrario, si può sempre passare un puntatore ad un oggetto List già assegnato, ed eseguire tutto ciò che l'inizializzazione length = 3 è un segnaposto per:

void init(List *l) { 
    l->length = 3; 
} 

List list; 
init(&list); 
printf("length final %d \n", list.length); 
+1

L'OP ha specificamente menzionato se è possibile evitarlo "void init (List ** l)" –

+0

@Gunner indirizzato – meagar

+0

L'assegnazione specifica che deve essere il costruttore. Ho intenzione di mandare un email al mio professore e chiedergli di cambiare i requisiti. Grazie per l'aiuto! –

3

Il problema è che il puntatore viene passato per valore, così sei le modifiche vengono scartate. Hai davvero bisogno di un puntatore a un puntatore per farlo correttamente. Come in si dovrebbe fare:

void init(List** l) { 
    *l = (List*) malloc(sizeof(List)); 
    // ... 
} 

E quando lo si chiama, si può usare al posto di init(&list)init(list). Naturalmente, in questo caso, ha senso andare avanti e restituire il risultato invece di utilizzare un puntatore a un puntatore:

List* init() { 
    List* result = (List *) malloc(sizeof(List)); 
    result->length = 3; 
    return result; 
} 

E poi, con quanto sopra, si può semplicemente utilizzare list = init();.

Si noti che in C++, è possibile utilizzare riferimenti anziché puntatori, ma mescolare riferimenti e puntatori è incredibilmente disordinato. Qui, usare un tipo di ritorno è davvero la cosa più bella da fare.

Se è assolutamente necessario utilizzare la firma esistente, è possibile essere subdoli e inizializzare l'elenco, quindi nella funzione init() è possibile impostare il successivo punto del puntatore dell'elenco passato all'elenco che si desidera effettivamente creare. Quindi, dopo che è stato chiamato il init(), è possibile prendere il puntatore successivo e smaltire l'oggetto elenco originale creato. Oppure puoi sempre avere il primo elemento con qualche elemento fittizio.

1

questo compito è probabilmente un buon modo per insegnare in una classe il passaggio per valore e passare per riferimento. Se si desidera mantenere la firma del costruttore è necessario modificare la funzione principale.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
typedef struct { 
    unsigned length; 
} List; 


void init(List *l) { 
    l->length = 3; 
} 
int main(void) { 
    List list;// x = NULL; 
    memset(&list,0,sizeof(List)); 
    init(&list); 
    printf("length final %d \n", list.length); 
    return 1; 
} 

Ora qui elenco è di tipo Elenco e non indirizzo a Elenco. il metodo init() ha passato l'indirizzo della lista e all'interno di init è possibile modificare il valore dei contenuti della struttura.

./a.out

lunghezza finale 3

+0

Ritengo che memset sia una violazione dell'incapsulamento. init è responsabile dell'inserimento del contenuto della lista; se è necessario un memset, dovrebbe farlo. Così com'è, non è necessario. –

0

init deve essere passato un puntatore a un elenco esistente. Sospetto che il vero problema qui sia nella struttura dei dati. Hai qualcosa chiamato Lista, che contiene una lunghezza, ma non c'è nessuna lista da vedere da nessuna parte. L'elenco dovrebbe probabilmente contenere un puntatore a un array della lunghezza specificata e init dovrebbe malloc quell'array e impostare il puntatore. Probabilmente lo scoprirai quando chiederai al professore di correggere i suoi requisiti che non sono infranti - se lo fossero, probabilmente avrebbe sentito parlare degli studenti passati e li avrebbe corretti ormai.

Problemi correlati