web-dev-qa-db-fra.com

Renvoyer un tableau en utilisant C

Je suis relativement nouveau en C et j’ai besoin d’aide pour les méthodes de traitement des tableaux. Venant de la programmation Java, je suis habitué à pouvoir dire int [] method() afin de retourner un tableau. Cependant, j'ai découvert qu'avec C, vous devez utiliser des pointeurs pour les tableaux lorsque vous les renvoyez. En tant que nouveau programmeur, je ne le comprends vraiment pas du tout, même avec les nombreux forums que j'ai consultés.

Fondamentalement, j'essaie d'écrire une méthode qui retourne un tableau de caractères en C. Je vais fournir à la méthode (appelons-la returnArray) un tableau. Il créera un nouveau tableau à partir du tableau précédent et lui renverra un pointeur. J'ai juste besoin d'aide pour savoir comment démarrer ceci et comment lire le pointeur une fois qu'il est envoyé hors du tableau. Toute aide expliquant ceci est appréciée.

Format de code proposé pour la fonction de retour de tablea

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

Appelant de la fonction

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

Je n'ai pas encore testé cela car mon compilateur C ne fonctionne pas pour le moment mais j'aimerais comprendre ceci

129
user1506919

Vous ne pouvez pas retourner de tableaux à partir de fonctions en C. Vous ne pouvez pas non plus (ne devriez pas) faire ceci:

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 est créé avec une durée de stockage automatique et les références à celle-ci ne seront plus valides une fois que sa portée de déclaration aura été quittée, c'est-à-dire lorsque la fonction reviendra.

Vous devrez allouer dynamiquement la mémoire à l'intérieur de la fonction ou remplir un tampon préalloué fourni par l'appelant.

Option 1:

allouer dynamiquement la mémoire à l'intérieur de la fonction (appelant responsable de la désallocation 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;
}

Appelez ça comme ça:

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

    return 0;
}

Option 2:

remplir un tampon préalloué fourni par l'appelant (l'appelant alloue buf et passe à la fonction)

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

Et appelez comme ça:

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)
}
193
Ed S.

Le traitement des tableaux par C est très différent de celui de Java, et vous devrez adapter votre pensée en conséquence. Les tableaux en C ne sont pas des objets de première classe (en d'autres termes, une expression de tableau ne conserve pas son "caractère de tableau" dans la plupart des contextes). En C, une expression de type "tableau à N éléments de T" sera implicitement convertie ("decay") en une expression de type "pointeur sur T", sauf si l'expression du tableau est un opérande des opérateurs sizeof ou unaire &, ou si l'expression du tableau est un littéral utilisé pour initialiser un autre tableau dans une déclaration.

Entre autres choses, cela signifie que vous ne pouvez pas transmettre une expression de tableau à une fonction et la faire recevoir en tant que type de tableau ; la fonction reçoit en réalité un type de pointeur:

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

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

Dans l'appel à foo, l'expression str est convertie du type char [6] en char *, raison pour laquelle le premier paramètre de foo est déclaré char *a au lieu de char a[6]. Dans sizeof str, comme l'expression du tableau est un opérande de l'opérateur sizeof, elle n'est pas convertie en un type de pointeur. Vous obtenez donc le nombre d'octets dans le tableau (6).

Si vous êtes vraiment intéressé, vous pouvez lire le texte de Dennis Ritchie Le développement du langage C pour comprendre d'où vient ce traitement. .

Le résultat est que les fonctions ne peuvent pas renvoyer de types de tableaux, ce qui est correct, car les expressions de tableau ne peuvent pas non plus être la cible d'une affectation.

La méthode la plus sûre consiste pour l'appelant à définir le tableau et à transmettre son adresse et sa taille à la fonction censée y écrire:

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

Une autre méthode consiste pour la fonction à allouer le tableau de manière dynamique et à renvoyer le pointeur et la taille:

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

Dans ce cas, l'appelant est responsable de la désallocation du tableau avec la fonction de bibliothèque free.

Notez que dst dans le code ci-dessus est un simple pointeur sur char, pas un pointeur sur un tableau de char. La sémantique du pointeur et du tableau de C est telle que vous pouvez appliquer l'opérateur d'indice [] à une expression de type tableau ou de type pointeur ; src[i] et dst[i] accèdent tous les deux au i 'ème élément du tableau (même si seul src a le type de tableau).

Vous pouvez déclarer un pointeur sur un tableau de N-éléments de T et faire quelque chose de similaire:

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

Plusieurs inconvénients avec ce qui précède. Tout d’abord, les anciennes versions de C s’attendent à ce que SOME_SIZE soit une constante de compilation, ce qui signifie que cette fonction ne fonctionnera jamais avec une seule taille de tableau. Deuxièmement, vous devez déréférencer le pointeur avant d'appliquer l'indice, ce qui encombrerait le code. Les pointeurs vers les tableaux fonctionnent mieux lorsque vous utilisez des tableaux multidimensionnels.

24
John Bode

Que diriez-vous de cette implémentation délicieusement perverse?

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

principal 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;
}
8
pyrospade

Je ne dis pas que c'est la meilleure solution ou une solution préférée au problème donné. Cependant, il peut être utile de rappeler que les fonctions peuvent renvoyer des structures. Bien que les fonctions ne puissent pas renvoyer de tableaux, les tableaux peuvent être encapsulés dans des structures et la fonction peut renvoyer la structure transportant ainsi le tableau. Cela fonctionne pour les tableaux de longueur fixe.

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

J'invite les commentaires sur les forces et les faiblesses de cette technique. Je n'ai pas pris la peine de le faire.

6
Indinfer

Dans votre cas, vous créez un tableau sur la pile et une fois que vous quittez la portée de la fonction, le tableau sera désalloué. Au lieu de cela, créez un tableau alloué dynamiquement et renvoyez un pointeur sur celui-ci.

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
Man of One Way

Vous pouvez le faire en utilisant la mémoire de tas (via malloc () invocation) comme les autres réponses rapportées ici, mais vous devez toujours gérer la mémoire (utilisez free () à chaque fois que vous appelez votre fonction). Vous pouvez aussi le faire avec un tableau statique:

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

// do something in your array here

return array; 
}

Vous pouvez ensuite l'utiliser sans vous soucier de la gestion de la mémoire.

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

Dans cet exemple, vous devez utiliser le mot clé static dans la définition de tableau pour définir la durée de vie du tableau sur une longueur d'application, afin qu'elle ne soit pas détruite après l'instruction return. Bien sûr, de cette façon, vous occupez des octets SIZE dans votre mémoire pendant toute la durée de vie de l’application, alors redimensionnez-le correctement!

4
mengo

Votre méthode retournera une variable de pile locale qui échouera mal. Pour renvoyer un tableau, créez-en un en dehors de la fonction, transmettez-le par adresse à la fonction, puis modifiez-le ou créez un tableau sur le tas et renvoyez cette variable. Les deux fonctionneront, mais le premier ne nécessite aucune allocation de mémoire dynamique pour le faire fonctionner correctement.

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
Michael Dorgan

Vous pouvez utiliser un code comme ceci:

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

Lorsque vous faites cela, la mémoire doit être libérée plus tard, en passant l'adresse à libérer.

Il y a d'autres options. Une routine peut renvoyer un pointeur sur un tableau (ou une partie d'un tableau) faisant partie d'une structure existante. L'appelant peut transmettre un tableau et la routine écrit simplement dans le tableau plutôt que d'allouer de l'espace pour un nouveau tableau.

0
Eric Postpischil