Domanda

Sto cercando di creare una funzione che accetta un array come argomento, aggiunge valori ad esso (aumentandone le dimensioni se necessario) e restituisce il conteggio degli elementi. Finora ho:

int main(int argc, char** argv) {
    int mSize = 10;
    ent a[mSize];
    int n;
    n = addValues(a,mSize);

    for(i=0;i<n;i++) {
       //Print values from a
    }
}

int addValues(ent *a, int mSize) {
    int size = mSize;

    i = 0;

    while(....) { //Loop to add items to array
        if(i>=size-1) { 
            size = size*2;
            a = realloc(a, (size)*sizeof(ent));
        }
        //Add to array
        i++;
    }
    return i;
}

Funziona se mSize è abbastanza grande da contenere tutti i potenziali elementi dell'array, ma se ha bisogno di essere ridimensionato, ottengo un errore di segmentazione.

Ho anche provato:

int main(int argc, char** argv) {
    ...
    ent *a;
    ...
}

int addValues(ent *a, int mSize) {
    ...
    a = calloc(1, sizeof(ent);
    //usual loop
    ...
}

Inutilmente.

Suppongo che ciò sia dovuto al fatto che quando chiamo realloc, la copia di 'a' viene indirizzata altrove - come è possibile modificarlo in modo che 'a' punti sempre nella stessa posizione?

Lo sto affrontando correttamente? Esistono modi migliori per gestire le strutture dinamiche in C? Dovrei implementare un elenco collegato per gestirli?

È stato utile?

Soluzione

Il problema principale qui è che stai cercando di usare realloc con un array allocato in pila. Hai:

ent a[mSize];

Questa è allocazione automatica nello stack. Se si desidera utilizzare realloc () in seguito, è necessario creare l'array sull'heap utilizzando malloc (), in questo modo:

ent *a = (ent*)malloc(mSize * sizeof(ent));

In modo che la libreria malloc (e quindi realloc (), ecc.) sia a conoscenza dell'array. A quanto pare, potresti confondere Array a lunghezza variabile C99 con veri array dinamici , quindi assicurati di aver compreso la differenza prima di provare a risolvere questo problema.

In realtà, tuttavia, se stai scrivendo array dinamici in C, dovresti provare a usare il design OOP-ish per incapsulare informazioni sugli array e nasconderli all'utente. Si desidera consolidare le informazioni (ad es. Puntatore e dimensioni) sull'array in una struttura e operazioni (ad es. Allocazione, aggiunta di elementi, rimozione di elementi, liberazione, ecc.) In funzioni speciali che funzionano con la propria struttura. Quindi potresti avere:

typedef struct dynarray {
   elt *data;
   int size;
} dynarray;

E potresti definire alcune funzioni per lavorare con i dynarrays:

// malloc a dynarray and its data and returns a pointer to the dynarray    
dynarray *dynarray_create();     

// add an element to dynarray and adjust its size if necessary
void dynarray_add_elt(dynarray *arr, elt value);

// return a particular element in the dynarray
elt dynarray_get_elt(dynarray *arr, int index);

// free the dynarray and its data.
void dynarray_free(dynarray *arr);

In questo modo l'utente non deve ricordare esattamente come allocare le cose o le dimensioni dell'array attualmente. Spero che ti inizi.

Altri suggerimenti

Prova a rielaborarlo in modo che venga passato un puntatore a un puntatore all'array, ovvero ent ** a . Quindi sarai in grado di aggiornare il chiamante nella nuova posizione dell'array.

questa è una buona ragione per usare OOP. sì, puoi fare OOP su C, e sembra anche bello se fatto correttamente.

in questo semplice caso non hai bisogno di ereditarietà né polimorfismo, solo i concetti di incapsulamento e metodi:

  • definisce una struttura con una lunghezza e un puntatore di dati. forse una dimensione dell'elemento.
  • scrive funzioni getter / setter che operano su puntatori a quella struttura.
  • la funzione 'grow' modifica il puntatore ai dati all'interno della struttura, ma qualsiasi puntatore alla struttura rimane valido.

Se hai modificato la dichiarazione della variabile in main in

ent *a = NULL;

il codice funzionerebbe più come avevi previsto non liberando un array allocato in pila. L'impostazione di a su NULL funziona perché realloc lo considera come se l'utente chiamasse malloc (size). Tieni presente che con questa modifica, il prototipo di addValue deve cambiare in

int addValues(ent **a, int mSize)

e che il codice deve gestire il caso di errore Realloc. Ad esempio

while(....) { //Loop to add items to array
    tmp = realloc(*a, size*sizeof(ent));
    if (tmp) {
        *a = tmp;
    } else {
        // allocation failed. either free *a or keep *a and
        // return an error
    }
    //Add to array
    i++;
}

Mi aspetto che la maggior parte delle implementazioni di realloc alloca internamente il doppio della memoria se il buffer corrente deve essere ridimensionato rendendo il codice originale

size = size * 2;

inutili.

Stai passando il puntatore ad array per valore. Ciò significa:

int main(int argc, char** argv) {
    ...
    ent *a; // This...
    ...
}

int addValues(ent *a, int mSize) {
    ...
    a = calloc(1, sizeof(ent); // ...is not the same as this
    //usual loop
    ...
}

pertanto la modifica del valore di a nella funzione addValues ?? non modifica il valore di a in main. Per modificare il valore di a in main devi passare un riferimento a addValues ??. Al momento, il valore di a viene copiato e passato a addValues ??. Per passare un riferimento a un uso:

int addValues (int **a, int mSize)

e chiamalo come:

int main(int argc, char** argv) {
    ...
    ent *a; // This...
    ...
    addValues (&a, mSize);
}

In addValues ??, accedi agli elementi di questo tipo:

(*a)[element]

e riallocare l'array in questo modo:

(*a) = calloc (...);

Xahtep spiega come il tuo chiamante può gestire il fatto che realloc () potrebbe spostare l'array in una nuova posizione. Finché lo fai, dovresti andare bene.

realloc () potrebbe diventare costoso se si inizia a lavorare con array di grandi dimensioni. È allora che è il momento di iniziare a pensare di utilizzare altre strutture di dati: un elenco collegato, un albero binario, ecc.

Come indicato, è necessario passare il puntatore al puntatore per aggiornare il valore del puntatore.
Ma suggerirei di ridisegnare ed evitare questa tecnica, nella maggior parte dei casi può e dovrebbe essere evitata. Senza sapere esattamente cosa stai cercando di ottenere, è difficile suggerire un design alternativo, ma sono sicuro al 99% che è fattibile in altro modo. E come Javier triste - pensa orientato agli oggetti e otterrai sempre un codice migliore.

Sei veramente obbligato a usare C? Questa sarebbe una grande applicazione del "std :: vector" di C ++, che è precisamente un array di dimensioni dinamiche (facilmente ridimensionabile con una sola chiamata non è necessario scrivere ed eseguire il debug).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top