Restituire un array usando C

Sono relativamente nuovo a C e ho bisogno di aiuto con i metodi che riguardano gli array. Venendo dalla programmazione Java, sono abituato a poter dire int [] method() per restituire un array. Tuttavia, ho scoperto che con C devi usare i puntatori per gli array quando li restituisci. Essendo un nuovo programmatore, davvero non lo capisco affatto, anche con i numerosi forum che ho guardato attraverso.

Fondamentalmente, sto provando a scrivere un metodo che restituisce un array di caratteri in C. Fornirò il metodo (chiamiamolo returnArray) con un array. Creerà una nuova matrice dall’array precedente e restituirà un puntatore ad essa. Ho solo bisogno di aiuto su come iniziare e come leggere il puntatore una volta che è stato mandato fuori dall’array. Qualsiasi aiuto che lo spieghi è apprezzato.

Formato di codice proposto per la funzione di ritorno della matrice

 char *returnArray(char array []){ char returned [10]; //methods to pull values from array, interpret them, and then create new array return &(returned[0]); //is this correct? } 

Chiamante della funzione

 int main(){ int i=0; char array []={1,0,0,0,0,1,1}; char arrayCount=0; char* returnedArray = returnArray(&arrayCount); ///is this correct? for (i=0; i<10;i++) printf(%d, ",", returnedArray[i]); //is this correctly formatted? } 

Non ho ancora provato questo come il mio compilatore C non sta funzionando al momento, ma mi piacerebbe capirlo

Non è ansible restituire gli array dalle funzioni in C. Non è ansible (non dovrebbe) eseguire ciò:

 char *returnArray(char array []){ char returned [10]; //methods to pull values from array, interpret them, and then create new array return &(returned[0]); //is this correct? } 

returned viene creato con la durata dell’archiviazione automatica e i riferimenti a esso diventano non validi una volta che lascia il suo ambito dichiarativo, cioè quando la funzione ritorna.

Sarà necessario allocare dynamicmente la memoria all’interno della funzione o riempire un buffer preallocato fornito dal chiamante.

Opzione 1:

allocare dynamicmente la memoria all’interno della funzione (chiamante responsabile per deallocating ret )

 char *foo(int count) { char *ret = malloc(count); if(!ret) return NULL; for(int i = 0; i < count; ++i) ret[i] = i; return ret; } 

Chiamalo così:

 int main() { char *p = foo(10); if(p) { // do stuff with p free(p); } return 0; } 

Opzione 2:

riempire un buffer preallocato fornito dal chiamante (il chiamante assegna buf e passa alla funzione)

 void foo(char *buf, int count) { for(int i = 0; i < count; ++i) buf[i] = i; } 

E chiamalo così:

 int main() { char arr[10] = {0}; foo(arr, 10); // No need to deallocate because we allocated // arr with automatic storage duration. // If we had dynamically allocated it // (ie malloc or some variant) then we // would need to call free(arr) } 

Il trattamento degli array di C è molto diverso da quello di Java e dovrai adattare il tuo pensiero di conseguenza. Le matrici in C non sono oggetti di prima class (cioè, un’espressione di matrice non mantiene la sua “matrice” nella maggior parte dei contesti). In C, un’espressione di tipo “array di elementi N di T ” sarà convertita implicitamente (“decay”) a un’espressione di tipo “puntatore a T “, tranne quando l’espressione dell’array è un operando di sizeof o unario & operatori oppure se l’espressione dell’array è una stringa letterale utilizzata per inizializzare un’altra matrice in una dichiarazione.

Tra le altre cose, ciò significa che non è ansible passare un’espressione di matrice a una funzione e riceverla come un tipo di matrice ; la funzione riceve effettivamente un tipo di puntatore:

 void foo(char *a, size_t asize) { // do something with a } int bar(void) { char str[6] = "Hello"; foo(str, sizeof str); } 

Nella chiamata a foo , l’espressione str viene convertita da type char [6] in char * , motivo per cui il primo parametro di foo viene dichiarato char *a invece di char a[6] . In sizeof str , poiché l’espressione array è un operando dell’operatore sizeof , non viene convertita in un tipo di puntatore, quindi si ottiene il numero di byte nell’array (6).

Se sei davvero interessato, puoi leggere Dennis Ritchie’s Lo sviluppo del linguaggio C per capire da dove viene questo trattamento.

Il risultato è che le funzioni non possono restituire tipi di array, il che va bene poiché le espressioni dell’array non possono essere il bersaglio di un compito.

Il metodo più sicuro è che il chiamante definisca la matrice e ne trasmetta l’indirizzo e la dimensione alla funzione che deve scrivere su di essa:

 void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize) { ... dstArray[i] = some_value_derived_from(srcArray[i]); ... } int main(void) { char src[] = "This is a test"; char dst[sizeof src]; ... returnArray(src, sizeof src, dst, sizeof dst); ... } 

Un altro metodo è per la funzione di allocare l’array in modo dinamico e restituire il puntatore e la dimensione:

 char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize) { char *dstArray = malloc(srcSize); if (dstArray) { *dstSize = srcSize; ... } return dstArray; } int main(void) { char src[] = "This is a test"; char *dst; size_t dstSize; dst = returnArray(src, sizeof src, &dstSize); ... free(dst); ... } 

In questo caso, il chiamante è responsabile della deallocazione dell’array con la funzione di libreria free .

Si noti che dst nel codice sopra è un semplice puntatore a char , non un puntatore a un array di char . Il puntatore e la semantica dell’array di C sono tali che è ansible applicare l’operatore subscript [] a un’espressione di tipo array o tipo puntatore; sia src[i] che dst[i] accederanno all’elemento i ‘th dell’array (anche se solo src ha un tipo di array).

Puoi dichiarare un puntatore a un array di elementi N di T e fare qualcosa di simile:

 char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE] { char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr); if (dstArr) { ... (*dstArr)[i] = ...; ... } return dstArr; } int main(void) { char src[] = "This is a test"; char (*dst)[SOME_SIZE]; ... dst = returnArray(src, sizeof src); ... printf("%c", (*dst)[j]); ... } 

Diversi inconvenienti con quanto sopra. Prima di tutto, le versioni precedenti di C si aspettano che SOME_SIZE sia una costante in fase di compilazione, il che significa che la funzione funzionerà sempre con una sola dimensione di array. In secondo luogo, è necessario dereferenziare il puntatore prima di applicare l’indice, che ingombra il codice. I puntatori agli array funzionano meglio quando hai a che fare con array multidimensionali.

Che ne dici di questa implementazione deliziosamente malvagia?

array.h

 #define IMPORT_ARRAY(TYPE) \ \ struct TYPE##Array { \ TYPE* contents; \ size_t size; \ }; \ \ struct TYPE##Array new_##TYPE##Array() { \ struct TYPE##Array a; \ a.contents = NULL; \ a.size = 0; \ return a; \ } \ \ void array_add(struct TYPE##Array* o, TYPE value) { \ TYPE* a = malloc((o->size + 1) * sizeof(TYPE)); \ TYPE i; \ for(i = 0; i < o->size; ++i) { \ a[i] = o->contents[i]; \ } \ ++(o->size); \ a[o->size - 1] = value; \ free(o->contents); \ o->contents = a; \ } \ void array_destroy(struct TYPE##Array* o) { \ free(o->contents); \ } \ TYPE* array_begin(struct TYPE##Array* o) { \ return o->contents; \ } \ TYPE* array_end(struct TYPE##Array* o) { \ return o->contents + o->size; \ } 

main.c

 #include  #include "array.h" IMPORT_ARRAY(int); struct intArray return_an_array() { struct intArray a; a = new_intArray(); array_add(&a, 1); array_add(&a, 2); array_add(&a, 3); return a; } int main() { struct intArray a; int* it; int* begin; int* end; a = return_an_array(); begin = array_begin(&a); end = array_end(&a); for(it = begin; it != end; ++it) { printf("%d ", *it); } array_destroy(&a); getchar(); return 0; } 

Nel tuo caso, stai creando una matrice sullo stack e una volta lasciato l’ambito della funzione, l’array verrà deallocato. Invece, creare una matrice allocata dynamicmente e restituire un puntatore ad essa.

 char * returnArray(char *arr, int size) { char *new_arr = malloc(sizeof(char) * size); for(int i = 0; i < size; ++i) { new_arr[i] = arr[i]; } return new_arr; } int main() { char arr[7]= {1,0,0,0,0,1,1}; char *new_arr = returnArray(arr, 7); // don't forget to free the memory after you're done with the array free(new_arr); } 

Non sto dicendo che questa sia la soluzione migliore o una soluzione preferita per il problema dato. Tuttavia, può essere utile ricordare che le funzioni possono restituire le strutture. Sebbene le funzioni non possano restituire array, gli array possono essere racchiusi in strutture e la funzione può restituire la struct portando così l’array con esso. Funziona con array di lunghezza fissa.

  #include  #include  #include  typedef struct { char v[10]; } CHAR_ARRAY; CHAR_ARRAY returnArray(CHAR_ARRAY array_in, int size) { CHAR_ARRAY returned; /* . . . methods to pull values from array, interpret them, and then create new array */ for (int i = 0; i < size; i++ ) returned.v[i] = array_in.v[i] + 1; return returned; // Works! } int main(int argc, char * argv[]) { CHAR_ARRAY array = {1,0,0,0,0,1,1}; char arrayCount = 7; CHAR_ARRAY returnedArray = returnArray(array, arrayCount); for (int i = 0; i < arrayCount; i++) printf("%d, ", returnedArray.v[i]); //is this correctly formatted? getchar(); return 0; } 

Invito commenti sui punti di forza e di debolezza di questa tecnica. Non mi sono preoccupato di farlo.

Puoi farlo usando la memoria heap (tramite invocazione malloc () ) come altre risposte riportate qui, ma devi sempre gestire la memoria (usa la funzione free () ogni volta che chiami la funzione). Puoi anche farlo con un array statico:

 char* returnArrayPointer() { static char array[SIZE]; // do something in your array here return array; } 

È ansible utilizzarlo senza preoccuparsi della gestione della memoria.

 int main() { char* myArray = returnArrayPointer(); /* use your array here */ /* don't worry to free memory here */ } 

In questo esempio è necessario utilizzare la parola chiave statica nella definizione dell’array per impostare la durata dell’array in modo che applichi l’applicazione, quindi non verrà distrutta dopo l’istruzione return. Naturalmente, in questo modo occupi i byte SIZE nella tua memoria per l’intera vita dell’applicazione, quindi ridimensionalo correttamente!

Il tuo metodo restituirà una variabile stack locale che avrà esito negativo. Per restituire un array, crearne uno al di fuori della funzione, passarlo per indirizzo nella funzione, quindi modificarlo o creare un array sull’heap e restituire tale variabile. Entrambi funzioneranno, ma il primo non richiede alcuna allocazione dynamic della memoria per farlo funzionare correttamente.

 void returnArray(int size, char *retArray) { // work directly with retArray or memcpy into it from elsewhere like // memcpy(retArray, localArray, size); } #define ARRAY_SIZE 20 int main(void) { char foo[ARRAY_SIZE]; returnArray(ARRAY_SIZE, foo); } 

Puoi usare un codice come questo:

 char *MyFunction(some arguments...) { char *pointer = malloc(size for the new array); if (!pointer) An error occurred, abort or do something about the error. return pointer; // Return address of memory to the caller. } 

Quando lo fai, la memoria dovrebbe essere liberata in seguito, facendo passare l’indirizzo libero.

Ci sono altre opzioni Una routine potrebbe restituire un puntatore a un array (o parte di un array) che fa parte di una struttura esistente. Il chiamante potrebbe passare un array e la routine si limita a scrivere nell’array anziché allocare lo spazio per un nuovo array.