Zwracanie tablicy za pomocą C

Jestem stosunkowo nowy w C i potrzebuję pomocy w metodach radzenia sobie z tablicami. Z programowania w Javie jestem przyzwyczajony do tego, że mogę powiedzieć int [] method(), aby zwrócić tablicę. Jednak dowiedziałem się, że w C musisz użyć wskaźników dla tablic, gdy je zwracasz. Będąc nowym programistą, naprawdę w ogóle tego nie rozumiem, nawet z wieloma forami, które przeglądałem.

Zasadniczo próbuję napisać metodę, która zwraca tablicę znaków w C. podam metoda (nazwijmy ją returnArray) z tablicą. Utworzy nową tablicę z poprzedniej tablicy i zwróci do niej wskaźnik. Potrzebuję tylko pomocy, jak to uruchomić i jak odczytać wskaźnik po wysłaniu go z tablicy. Każda pomoc w wyjaśnieniu tego jest doceniana.

Proponowany Format kodu dla funkcji zwracającej tablicę

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

Wywołanie funkcji

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

nie testowałem tego jeszcze, ponieważ mój kompilator C nie działa w tej chwili ale chciałbym to rozgryźć

Author: fortunate_man, 2012-07-25

8 answers

Nie możesz zwracać tablic z funkcji w C. nie możesz (nie powinieneś) tego zrobić:

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 jest tworzony z automatycznym czasem przechowywania i odniesienia do niego staną się nieważne, gdy opuści swój deklarowany zakres, tzn. gdy funkcja powróci.

Będziesz musiał dynamicznie przydzielać pamięć wewnątrz funkcji lub wypełnić wstępnie przydzielony bufor dostarczony przez wywołującego.

Opcja 1:

Dynamicznie przydzielaj pamięć wewnątrz function (caller responsible for 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;
}

Nazwij to tak:

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

    return 0;
}

Opcja 2:

Wypełnić wstępnie przydzielony bufor dostarczony przez wywołującego (caller przydziela buf i przekazuje do funkcji)

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

I nazwij to tak:

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)
}
 164
Author: Ed S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-06 09:53:11

Traktowanie tablic C jest Bardzo Inne od Javy i będziesz musiał odpowiednio dostosować swoje myślenie. Tablice w C nie są obiektami pierwszej klasy (tzn. wyrażenie tablicy nie zachowuje swojej "tablicy-ness" w większości kontekstów). W C wyrażenie typu "N-element array of T" będzie niejawnie konwertowane ("decay") na wyrażenie typu "pointer to T", z wyjątkiem sytuacji, gdy wyrażenie tablicy jest operandem operatorów sizeof lub unary &, lub jeśli tablica wyrażenie jest literał Łańcuchowy używany do inicjalizacji innej tablicy w deklaracji.

Między innymi oznacza to, że nie można przekazać wyrażenia tablicy do funkcji i otrzymać jako typ tablicy ; funkcja faktycznie otrzymuje typ wskaźnika:

void foo(char *a, size_t asize)
{
  // do something with a
}

int bar(void)
{
  char str[6] = "Hello";
  foo(str, sizeof str);
}

W wywołaniu foo wyrażenie str jest konwertowane z Typu char [6] na char *, dlatego pierwszy parametr foo jest zadeklarowany char *a zamiast char a[6]. W sizeof str, ponieważ tablica wyrażenie jest operandem operatora sizeof, nie jest przekonwertowane na typ wskaźnika, więc otrzymujemy liczbę bajtów w tablicy (6).

Jeśli jesteś naprawdę zainteresowany, możesz przeczytać Dennis Ritchie rozwój języka C , aby zrozumieć, skąd bierze się to leczenie.

Wynik jest taki, że funkcje nie mogą zwracać typów tablic, co jest w porządku, ponieważ wyrażenia tablicowe również nie mogą być celem przypisania.

Najbezpieczniejszy metoda służy do definiowania tablicy i przekazywania jej adresu i rozmiaru do funkcji, która ma do niej zapisywać:

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

Inną metodą jest dla funkcji przydzielanie tablicy dynamicznie i zwracanie wskaźnika i rozmiaru:

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

W tym przypadku wywołujący jest odpowiedzialny za dealokację tablicy za pomocą funkcji bibliotecznej free.

Zauważ, że dst w powyższym kodzie jest prosty wskaźnik do char, a nie wskaźnik do tablicy char. Wskaźnik C semantyka tablicy jest taka, że można zastosować operator indeksu dolnego [] do wyrażenia typu array lub typu wskaźnika; zarówno src[i], jak i dst[i] uzyskają dostęp do i'tego elementu tablicy (nawet jeśli tylko src mA typ array).

Możesz zadeklarować wskaźnik do tablicy N-elementów T i zrobić coś podobnego:

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

Kilka wad z powyższych. Po pierwsze, starsze wersje C oczekują, że SOME_SIZE będzie stałą czasu kompilacji, oznacza to, że funkcja będzie działać tylko z jednym rozmiarem tablicy. Po drugie, musisz zdereferować wskaźnik przed zastosowaniem indeksu dolnego, który zaśmieca kod. Wskaźniki do tablic działają lepiej, gdy masz do czynienia z tablicami wielowymiarowymi.

 21
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-25 19:59:13

Co powiesz na to cudownie złe wdrożenie?

/ align = "left" / 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;
}
 7
Author: pyrospade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-10 18:26:19

W Twoim przypadku tworzysz tablicę na stosie i po opuszczeniu zakresu funkcji tablica zostanie dealokowana. Zamiast tego utwórz dynamicznie przydzielaną tablicę i zwróć do niej wskaźnik.

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

}
 6
Author: Man of One Way,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 20:56:38

Nie mówię, że jest to najlepsze rozwiązanie lub preferowane rozwiązanie danego problemu. Warto jednak pamiętać, że funkcje mogą zwracać struktury. Chociaż funkcje nie mogą zwracać tablic, tablice mogą być zawinięte w struktury, a funkcja może zwracać strukturę, przenosząc tablicę ze sobą. Działa to dla tablic o stałej długości.

    #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;
    }
Zapraszam do komentowania mocnych i słabych stron tej techniki. Nie pofatygowałem się tym.
 5
Author: Indinfer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-10 17:57:27

Możesz to zrobić używając pamięci sterty (poprzez malloc () invocation), tak jak inne odpowiedzi podane tutaj, ale zawsze musisz zarządzać pamięcią (użyj free () za każdym razem, gdy wywołujesz swoją funkcję). Można to również zrobić za pomocą tablicy statycznej:

char* returnArrayPointer() 
{
static char array[SIZE];

// do something in your array here

return array; 
}

Można go używać bez martwienia się o zarządzanie pamięcią.

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

W tym przykładzie musisz użyć statycznego słowa kluczowego w definicji tablicy, aby ustawić na application-long żywotność tablicy, więc nie zostanie ona zniszczona po oświadczenie zwrotne. Oczywiście w ten sposób zajmujesz rozmiar bajtów w pamięci przez cały okres użytkowania aplikacji,więc odpowiednio ją rozmiar!

 3
Author: mengo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-26 16:11:36

Twoja metoda zwróci zmienną lokalnego stosu, która zawiedzie. Aby zwrócić tablicę, Utwórz tablicę poza funkcją, przekaż ją za pomocą adresu do funkcji, a następnie zmodyfikuj ją lub utwórz tablicę na stercie i zwróć tę zmienną. Oba będą działać, ale pierwszy nie wymaga żadnej dynamicznej alokacji pamięci, aby działał poprawnie.

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);
}
 2
Author: Michael Dorgan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-25 18:50:56

Możesz użyć kodu w następujący sposób:

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

Kiedy to zrobisz, pamięć powinna zostać później uwolniona, przekazując adres do free.

Są inne opcje. Funkcja może zwrócić wskaźnik do tablicy (lub jej części), która jest częścią istniejącej struktury. Wywołujący może przekazać tablicę, a procedura tylko zapisuje do tablicy, zamiast przydzielać miejsce na nową tablicę.

 0
Author: Eric Postpischil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-25 18:54:20