IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

FAQ C et erreurs courantes

Ce ou ces sujets font partie d'une FAQ en cours d'écriture, vos retours sont les bienvenus.

Retourner un tableau n'est pas possible en C. Le mieux que l'on puisse faire est de retourner une adresse.

Toutefois il faut être très prudent par rapport à la provenance de cette adresse. En effet, un tableau de taille fixe étant implicitement convertible en pointeur, on serait vite tenté de retourner son adresse :

 
Sélectionnez
int * fonction(void) {
    int tableau[15];
    // … Traitement …
    return tableau;
}

Mais il faut savoir que ce tableau int tableau[15], ainsi que toute variable dite « automatique » ou « locale » déclarée dans la fonction, seront détruits dès que l'on sortira de cette dernière. Cette adresse jusqu'alors occupée par le tableau sera ainsi libérée et pourra dès lors être éventuellement occupée par une variable nouvellement créée ou être utilisée par un autre programme. Toute tentative d'accès ou d'écriture à cette zone mémoire mènera à un comportement non définiQu'est-ce qu'un « comportement indéfini » (Undefined Behaviour / « UB ») ?. Cf. le sujet function returns address of local variablefunction returns address of local variable [-Werror=return-local-addr] .

Il existe quatre solutions :

  1. Si le tableau ne doit subir aucun changement d'adresse ou redimensionnement au sein de la fonction, juste ses valeurs qui sont lues/modifiées : passer un tableau déjà existant en paramètre, nous aurons probablement aussi besoin de passer sa taille en même temps.

    Code de la fonction Utilisation de la fonction
     
    Sélectionnez
    void fonction(int *tableau, size_t taille) {
        assert(tableau != NULL); // Le pointeur doit être valide
        // … Traitement …
    }

    On peut éventuellement retourner son adresse, ce tableau subsiste après être sorti de la fonction :

     
    Sélectionnez
    int * fonction(int *tableau, size_t taille) {
        assert(tableau != NULL); // Le pointeur doit être valide
        // … Traitement …
        return tableau;
    }

    On observe ce comportement dans la fonction standard strcat par exemple.

     
    Sélectionnez
    int tableau[15];
    fonction(tableau, ARRAY_SIZE(tableau));
    // Utilisation du tableau

    Ou avec un tableau dynamique :

     
    Sélectionnez
    int taille = 15;
    int *tableau = malloc(taille * sizeof(*tableau));
    if (tableau != NULL) {
        fonction(tableau, taille);
        // Utilisation du tableau
        free(tableau);
    } else {
        // Gestion de l'erreur
    }
  2. Si le tableau doit vraiment être créé dans la fonction, utiliser un tableau dynamique alloué avec la fonction malloc() : la zone mémoire allouée subsistera jusqu'à ce que vous appeliez free dessus.

    Code de la fonction Utilisation de la fonction
     
    Sélectionnez
    int * fonction() {
        int *tableau = malloc(15 * sizeof(*tableau));
        if (tableau != NULL) {
            // … Traitement …
        }
        return tableau;
    }
     
    Sélectionnez
    int *tableau = fonction();
    if (tableau != NULL) {
        // Utilisation du tableau
        free(tableau); // Nous n'avons plus besoin du tableau
    } else {
        // Gestion de l'erreur
    }

    Vous pourrez avoir besoin de connaître la taille du tableau alloué à l'intérieur de cette fonction, vous pouvez pour ce faire passer un pointeur à la fonction :

    Code de la fonction Utilisation de la fonction
     
    Sélectionnez
    int * fonction(size_t *size) {
        int *tableau = malloc(15 * sizeof(*tableau));
        if (tableau != NULL) {
            *size = 15;
            // … Traitement …
        }
        return tableau;
    }
     
    Sélectionnez
    size_t size = 0;
    int *tableau = fonction(&size);
    if (tableau != NULL) {
        // Utilisation du tableau
        free(tableau); // Nous n'avons plus besoin du tableau
    } else {
        // Gestion de l'erreur
    }

    ou plus simple, utiliser une structure regroupant pointeur et taille qu'il vous suffira de retourner :

     
    Sélectionnez
    struct vector {
        int *data;
        size_t size;
    };
  3. Passer l'adresse d'un pointeur en paramètre, vous aurez alors tout le loisir de le modifier comme bon vous semble :

    Code de la fonction Utilisation de la fonction
     
    Sélectionnez
    int fonction(int **ptableau) {
        assert(ptableau != NULL); // Le double pointeur doit être valide
        int *tab = malloc(15 * sizeof(*tableau));
        if (tab == NULL) {
            return EXIT_FAILURE;
        }
        // … Traitement …
        *ptableau = tab; // Je suppose que *ptableau ne pointe par sur une 
                         // zone allouée ou il faudrait d'abord libérer cet
                         // emplacement, sinon nous aurions une fuite de mémoire
        return EXIT_SUCCESS;
    }
     
    Sélectionnez
    int *tableau = NULL;
    if (!fonction(&tableau)) {
        // Utilisation du tableau
        free(tableau); // Nous n'avons plus besoin du tableau
    } else {
        // Gestion de l'erreur
    }
  4. (Méthode déconseillée : la fonction ne sera pas réentrante) utiliser un tableau static dont on renvoie l'adresse :

     
    Sélectionnez
    int * fonction() {
        static int tableau[15];
        // … Traitement …
        return tableau;
    }

    À noter qu'étant static, le tableau restera le même pendant toute la durée du programme, il ne sera pas renouvelé à chaque appel.

  5. Dernière méthode, valable pour une chaîne de caractères écrite "en dur" dans le programme non modifiable (➝ const nécessaire sous peine d'UBQu'est-ce qu'un « comportement indéfini » (Undefined Behaviour / « UB ») ?, car couramment stockée dans le read-only data segment [ou .rodata]) :

    Code de la fonction Utilisation de la fonction
     
    Sélectionnez
    char const * fonction(size_t index) {
        char const *noms[] = {"Jean", "Martin", "Alexandre", "Jack"};
        assert(index < ARRAY_SIZE(noms)); // on s'assure que l'indice ne 
                                          // dépasse pas la taille du tableau
        return noms[index];
    }
     
    Sélectionnez
    char const *nom = fonction(2);
    printf("%s\n", nom); // Alexandre
Créé le 13 décembre 2017  par Winjerome

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2022 Winjerome. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.