Bonjour,
J'aimerai retourné un tableau depuis une fonction comme ceci mais ça ne semble pas fonctionner :
Code:
1
2
3
4
5
6
7
8
9
10
11 char *test() { char *tab[] = { "un", "deux", "trois" }; return *tab; }
Version imprimable
Bonjour,
J'aimerai retourné un tableau depuis une fonction comme ceci mais ça ne semble pas fonctionner :
Code:
1
2
3
4
5
6
7
8
9
10
11 char *test() { char *tab[] = { "un", "deux", "trois" }; return *tab; }
Deux erreurs:
- Mauvais type de retour
- Tentative de retourner l'adresse d'une variable locale.
Pour retourner un tableau, pas trente-six solutions:
- Recevoir en paramètre l'adresse du tableau (ou de son premier élément) et le remplir
- Allouer dynamiquement (avec malloc(), quoi) le tableau et retourner son pointeur
- /!\Déconseillé : Retourner une structure contenant un tableau (taille fixe). Mais comme il est déconseillé de retourner directement une structure volumineuse...
Tu ne peux pas retourner un tableau alloué statiquement sur la pile de cette manière. Soit tu fait une allocation dynamique, soit tu passes ton tableau en paramètre.
Pour l'allocation dynamique (ne pas oublier de libérer la mémoire utilisée par le tableau dans la fonction appelante):
Avec passage du tableau en paramètre:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include <stdlib.h> #define NB_ELEMS(a) ( sizeof (a) / sizeof *(a) ) char const **test(void) { char const *tab[] = {"un", "deux", "trois"}; char const **p_return = NULL; p_return = malloc((NB_ELEMS(tab) + 1) * sizeof *p_return); if (p_return != NULL) { size_t i; for (i = 0; i < NB_ELEMS(tab); ++i) { p_return[i] = tab[i]; } p_return[i] = NULL; } return *p_return; }
Il est également possible de retourner l'adresse d'un tableau déclaré statique, mais cela peut engendrer certains problèmes.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 #define NB_ELEMS(a) ( sizeof (a) / sizeof *(a) ) char *test(char const *tab_out[], size_t taille) { int rv = 0; char const *tab[] = {"un", "deux", "trois"}; if (taille >= NB_ELEMS(tab)) { size_t i; /* -tc- initialisation du tableau */ for (i = 0; i < taille; i++) { tab_out[i] = NULL; } for (i = 0; i < NB_ELEMS(tab); i++) { tab_out[i] = tab[i]; } rv = i; } return rv; }
Thierry
Bonjour,
Merci pour les réponses.
>Thierry, ton 1er code renvoie des erreurs à la compilation (gcc) :
Code:
1
2
3
4
5 test.c:19: erreur: assignment of read-only location test.c:19: attention : assignment makes integer from pointer without a cast test.c:21: erreur: assignment of read-only location test.c:21: attention : assignment makes integer from pointer without a cast test.c:24: attention : return discards qualifiers from pointer target type
Et le 2éme un warning sur le return :
Code:test.c:28: attention : return makes pointer from integer without a cast
J'ai essayé de corriger le 1er code, es ce que c'est bon ?
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 #include <stdio.h> #include <stdlib.h> #define NB_ELEMS(a) ( sizeof (a) / sizeof *(a) ) char *test(void) { char *tab[] = {"un", "deux", "trois"}; char **p_return = NULL; p_return = malloc((NB_ELEMS(tab) + 1) * sizeof *p_return); if (p_return != NULL) { size_t i; for (i = 0; i < NB_ELEMS(tab); i++) { p_return[i] = tab[i]; } //p_return[i] = NULL; } return *p_return; }
Ce code est faux. Attention à bien tester le code avant de le publier...
Ceci fonctionne :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 #include <stdio.h> #include <stdlib.h> #define NB_ELEMS(a) ( sizeof (a) / sizeof *(a) ) char const **test (void) { char const *tab[] = { "un", "deux", "trois" }; char const **p_return = malloc ((NB_ELEMS (tab) + 1) * sizeof *p_return); if (p_return != NULL) { size_t i; for (i = 0; i < NB_ELEMS (tab); i++) { p_return[i] = tab[i]; } p_return[i] = NULL; } return p_return; } int main (void) { char const **pp = test (); if (pp != NULL) { size_t i = 0; while (pp[i] != NULL) { printf ("%u: %s\n", (unsigned) i, pp[i]); i++; } printf ("%u: %s\n", (unsigned) i, pp[i]); free (pp), pp = NULL; } return 0; }
Mais son caractère 'hybride' (tableau dynamique de pointeurs sur des chaines littérales) en fait un objet pas facile à utiliser et peu réaliste... Difficile à cerner pour un débutant à cause des 'const'.Code:
1
2
3
4
5
6
7 0: un 1: deux 2: trois 3: (null) Press ENTER to continue.
On doit pouvoir faire plus simple pour montrer comment "retourner" un tableau dynamique.
Euh, Je suis un peu surpris de ton exemple, il est potentiellement dangereux car il peut induire en erreur, il me semble.
Celà ne fonctione que parce que les éléments de tab sont des chaînes de caractères en dur, donc allouées dans des zones hors du code de la fonction (je ne sais pas si je suis très clair là :oops:).
[edit]
PS je viens de lire la fin de ton post, ça m'étonnait aussi un peu.
[/edit]
Merci bien.
Ben j'ai tout compris sauf les const pourquoi on peu pas metre de "char **p_return" au lieu "char const **p_return" ? et pourquoi const si c'est pour allouer dynamiquement ?
Ce const concerne la partie pointée. Or, par rapport au tableau original, on a rien changé et on pointe toujours vers des zones de textes initialisées et non modifiables.
Ce qui a été alloué dynamiquement, c'est le tableau de pointeurs. La valeur des pointeurs est encore l'adresse des premiers caractères des chaines.
J'ai conservé l'intention initiale de Thierry.
Oula, ça m'apprendra à faire mille choses à la fois, à écrire du code en vitesse et (chose rare) à ne pas tester avant de poster. J'ai corrigé mon post. Merci Emmanuel pour les corrections.
Oui, j'admets que l'exemple était mal choisi. Je suis parti du post du PO pour le construire... Travailler avec des entiers aurait été plus simple dans un premier temps pour montrer le principe...
Pour me faire pardonner voici un exemple plus simple à comprendre avec un tableau d'entiers:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 #include <stdio.h> #include <stdlib.h> /** * -tc- Alloue dynamiquement un tableau de taille entiers et initialise * chaque case du tableau. */ int *test_tableau_entiers(size_t taille) { int *p_return = NULL; /* -tc- si taille est inferieure a 1, il n'y a pas de tableau */ if (taille > 0) { p_return = malloc(taille * sizeof *p_return); /* -tc- on verifie toujours la valeur retournee par malloc() */ if (p_return != NULL) { size_t i; /* -tc- initialisation du tableau */ for (i = 0; i < taille; i++) { p_return[i] = i; } } } return p_return; } /* -tc- programme de test */ int main(void) { const size_t taille = 10; int ret = EXIT_SUCCESS; int *tableau = test_tableau_entiers(taille); if (tableau != NULL) { size_t i; for (i = 0; i < taille; i++) { printf("%d\n", tableau[i]); } free(tableau), tableau = NULL; } else { ret = EXIT_SUCCESS; } return ret; }
Puis voici une manière propre de travailler avec les tableaux de chaines de caractères (plus avancé):
ThierryCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 #include <stdio.h> #include <stdlib.h> #include <string.h> /* -tc- macro qui determine le nombre d'elements d'un tableau alloue statiquement */ #define NB_ELEMS(a) ( sizeof (a) / sizeof *(a) ) /** * -tc- retourne un tableau de chaines de caracteres alloue dynamiquement. * Le dernier element du tableau vaut NULL. */ char **test_tableau_chaines(void) { static char const *tab[] = {"un", "deux", "trois", "quatre", "cinq"}; char **p_return = NULL; p_return = malloc((NB_ELEMS(tab) + 1) * sizeof *p_return); /* -tc- TOUJOURS verifier la valeur retournee par malloc() */ if (p_return != NULL) { size_t i; int err = 0; for (i = 0; i < NB_ELEMS(tab) && err == 0; ++i) { /* -tc- on alloue la memoire et on copie chaque chaine N.B. Cette operation peut etre realisee beaucoup plus simple avec strdup() qui n'est pas standard, mais tres portable */ p_return[i] =malloc((strlen(tab[i]) + 1) * sizeof *p_return[i]); if (p_return[i] != NULL) { strcpy(p_return[i], tab[i]); } else { err = 1; } } /* -tc- en cas d'erreur, on fait le menage */ if (err != 0) { while (i > 0) { i--; /* -tc- on libere la memoire de chaque element du tableau */ free(p_return[i]); } /* -tc- on libere la memoire du tableau */ free(p_return), p_return = NULL; } else { /* -tc- on affecte la valeur NULL au dernier element du tableau */ p_return[i] = NULL; } } return p_return; } void test_tableau_chaines_free(char ***arr) { if (arr != NULL && *arr != NULL) { char **pp_self = *arr; size_t i; for (i = 0; pp_self[i] != NULL; ++i) { free(pp_self[i]); } free(pp_self); *arr = NULL; } } int main(void) { char **pp = NULL; int ret = EXIT_SUCCESS; pp = test_tableau_chaines(); if (pp != NULL) { size_t i = 0; while (pp[i] != NULL) { printf ("%u: %s\n", (unsigned) i, pp[i]); i++; } printf ("%u: %s\n", (unsigned) i, pp[i]); test_tableau_chaines_free(&pp); } else { ret = EXIT_FAILURE; } return ret; }
Les pointeurs sont allouées dynamiquement, mais les chaînes pointées sont des chaînes littérales, en lecture seule.
C'est pourquoi gcc propose l'option -Wwrite-strings qui donne un warning si tu essaies de pointer sur une chaîne littérale avec un pointeur non-const...
Es-ce que l'on est obligé d'utiliser const avec les tableaux dynamique de pointeurs sur char à chaque fois ou c'est juste dans l'exemple plus haut ?
ok merci pour toutes ces précisions.