Een array retourneren met C

Ik ben relatief nieuw voor C en ik heb wat hulp nodig met methoden die met arrays omgaan. Afkomstig uit Java-programmering, ben ik gewend om int [] method() te zeggen om een ​​array te retourneren. Ik heb echter ontdekt dat je met C pointers voor arrays moet gebruiken wanneer je ze retourneert. Omdat ik een nieuwe programmeur ben, begrijp ik dit echt helemaal niet, zelfs niet met de vele forums die ik heb doorgekeken.

Kortom, ik probeer een methode te schrijven die een char-array retourneert in C. Ik zal de methode (laten we het returnArray noemen) voorzien van een array. Het maakt een nieuwe array van de vorige array en retourneert er een aanwijzer naar. Ik heb alleen wat hulp nodig om dit op gang te krijgen en hoe de aanwijzer te lezen zodra deze uit de array is verzonden. Alle hulp bij het uitleggen hiervan wordt op prijs gesteld.

Voorgesteld codeformaat voor array-retourfunctie

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?
} 

Beller van de functie

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?
}

Ik heb dit nog niet getest omdat mijn C-compiler momenteel niet werkt, maar ik zou hier graag achter willen komen


Antwoord 1, autoriteit 100%

Je kunt geen arrays retourneren van functies in C. Je kunt dit ook niet (moeten) doen:

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 wordt gemaakt met automatische opslagduur en verwijzingen ernaar worden ongeldig zodra het zijn declaratiebereik verlaat, d.w.z. wanneer de functie terugkeert.

U moet het geheugen binnen de functie dynamisch toewijzen of een vooraf toegewezen buffer vullen die door de aanroeper wordt geleverd.

Optie 1:

het geheugen binnen de functie dynamisch toewijzen (aanroeper die verantwoordelijk is voor het ongedaan maken van de toewijzing van 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;
}

Noem het zo:

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

Optie 2:

vul een vooraf toegewezen buffer in die door de beller is verstrekt (beller wijst buf toe en geeft door aan de functie)

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

En noem het zo:

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
    // (i.e. malloc or some variant) then we 
    // would need to call free(arr)
}

Antwoord 2, autoriteit 12%

C’s behandeling van arrays is heel anders dan die van Java, en u zult uw manier van denken dienovereenkomstig moeten aanpassen. Arrays in C zijn geen eersteklas objecten (dat wil zeggen, een array-expressie behoudt in de meeste contexten niet zijn “array-heid”). In C wordt een uitdrukking van het type “N-element array van T” impliciet geconverteerd (“decay”) naar een uitdrukking van het type “pointer to T“, behalve wanneer de array-expressie een operand is van de sizeof of unaire & operators, of als de array-expressie een letterlijke tekenreeks is die wordt gebruikt om een ​​andere array in een declaratie te initialiseren.

Dit betekent onder andere dat je geen array-expressie aan een functie kunt doorgeven en deze als arraytype kunt laten ontvangen; de functie krijgt eigenlijk een aanwijzertype:

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

In de aanroep van foo wordt de uitdrukking str geconverteerd van het type char [6] naar char *, daarom wordt de eerste parameter van foo gedeclareerd als char *a in plaats van char a[6]. Aangezien de array-expressie in sizeof str een operand is van de operator sizeof, wordt deze niet geconverteerd naar een pointertype, dus je krijgt het aantal bytes in de array (6 ).

Als je echt geïnteresseerd bent, kun je De ontwikkeling van de C-taal om te begrijpen waar deze behandeling vandaan komt.

Het resultaat is dat functies geen arraytypes kunnen retourneren, wat prima is, aangezien array-expressies ook niet het doel van een toewijzing kunnen zijn.

De veiligste methode is dat de aanroeper de array definieert en het adres en de grootte doorgeeft aan de functie die ernaar moet schrijven:

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);
  ...
}

Een andere methode is dat de functie de array dynamisch toewijst en de aanwijzer en grootte retourneert:

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 dit geval is de aanroeper verantwoordelijk voor het ongedaan maken van de toewijzing van de array met de free bibliotheekfunctie.

Merk op dat dst in de bovenstaande code een eenvoudige verwijzing naar char is, niet een verwijzing naar een array van char. De pointer- en array-semantiek van C is zodanig dat je de subscript-operator [] kunt toepassen op een expressie van het array-type of pointertype; zowel src[i] als dst[i] hebben toegang tot het i‘de element van de array (hoewel alleen src heeft een array-type).

Je kunt een pointer declareren naar een N-element array van T en iets soortgelijks doen:

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]);
  ...
}

Verschillende nadelen van bovenstaande. Ten eerste verwachten oudere versies van C dat SOME_SIZE een constante tijdens het compileren is, wat inhoudt dat de functie altijd maar met één arraygrootte zal werken. Ten tweede moet u de verwijzing naar de aanwijzer verwijderen voordat u het subscript toepast, waardoor de code onoverzichtelijk wordt. Aanwijzers naar arrays werken beter als je te maken hebt met multidimensionale arrays.


Antwoord 3, autoriteit 6%

Ik zeg niet dat dit de beste oplossing of een voorkeursoplossing is voor het gegeven probleem. Het kan echter handig zijn om te onthouden dat functies structs kunnen retourneren. Hoewel functies geen arrays kunnen retourneren, kunnen arrays worden verpakt in structs en de functie kan de struct retourneren en daarbij de array met zich meedragen. Dit werkt voor arrays met een vaste lengte.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    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;
    }

Ik nodig opmerkingen uit over de sterke en zwakke punten van deze techniek. Ik heb niet de moeite genomen om dit te doen.


Antwoord 4, autoriteit 4%

Wat dacht je van deze heerlijk kwaadaardige implementatie?

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 <stdlib.h>
#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;
}

Antwoord 5, autoriteit 3%

Je kunt het met heap-geheugen doen (via malloc()-aanroep) zoals andere antwoorden die hier worden vermeld, maar je moet altijd het geheugen beheren (gebruik de functie free() elke keer dat u uw functie aanroept).
Je kunt het ook doen met een statische array:

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

Je kunt het dan gebruiken zonder je zorgen te maken over geheugenbeheer.

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

In dit voorbeeld moet u het statische sleutelwoord in de arraydefinitie gebruiken om de levensduur van de array op toepassingslang in te stellen, zodat deze niet wordt vernietigd na de return-instructie.
Op deze manier neemt u natuurlijk SIZE bytes in uw geheugen voor de gehele levensduur van de applicatie, dus de juiste maat nemen!


Antwoord 6, autoriteit 2%

In uw geval maakt u een array op de stapel en zodra u het functiebereik verlaat, wordt de toewijzing van de array ongedaan gemaakt. Maak in plaats daarvan een dynamisch toegewezen array en stuur er een pointer naar terug.

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);
}

Antwoord 7

Uw methode retourneert een lokale stackvariabele die slecht zal mislukken. Als u een array wilt retourneren, maakt u er een buiten de functie, geeft u deze per adres door aan de functie, wijzigt u deze of maakt u een array op de heap en retourneert u die variabele. Beide zullen werken, maar de eerste vereist geen dynamische geheugentoewijzing om het correct te laten werken.

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);
}

Antwoord 8

Je kunt de code als volgt gebruiken:

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.
}

Als je dit doet, moet het geheugen later worden vrijgemaakt door het adres door te geven aan vrij.

Er zijn andere opties. Een routine kan een aanwijzer terugsturen naar een array (of een deel van een array) die deel uitmaakt van een bestaande structuur. De aanroeper kan een array doorgeven, en de routine schrijft alleen in de array, in plaats van ruimte toe te wijzen voor een nieuwe array.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

nineteen − 10 =

Other episodes