Bonjour,
Vous pouvez postez ici vos avis, remarques, suggestions sur l'article suivant :
http://rperrot.developpez.com/articles/c/allocationC/
NB: Merci de lire ce message avant de poster.
Bonjour,
Vous pouvez postez ici vos avis, remarques, suggestions sur l'article suivant :
http://rperrot.developpez.com/articles/c/allocationC/
NB: Merci de lire ce message avant de poster.
Bonsoir,
Juste pour vous signaler une petite erreur dans un code de votre tutoriel :
La condition de la boucle for est mauvaise : elle s’arrêtera au troisième élément du tableau, et n'affichera donc pas tabentier[3] = 4, comme attendu.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 int * tabentier; /* Création d'un tableau de 3 entiers */ tabentier = calloc ( 3 , sizeof(int) ); tabentier[0] = 1; tabentier[1] = 2; tabentier[2] = 3; /* Ajout d'un element au tableau */ tabentier = realloc (tabentier, 4 * sizeof(int) ); tabentier[3] = 4; for ( i = 0 ; i < 3 ; i++ ) { printf(" tabentier[%d] = %d \n", i , tabentier[i] ); }
Merci.
Bonne continuation,
neow_
Je trouve le tutoriel excellent. Cependant, il y a un problème avec les macros MALLOC, CALLOC et REALLOC. Prenons ce code tiré du tutoriel par exemple :
Si CALLOC echoue disons alors qu'on en est à i = 2, matrice[2] va certes être libéré mais le programme se termine tout de suite : matrice[1], matrice[0] et matrice n'ont pas été libérés ! Il faut donc soit les retirer du tutoriel, soit les corriger.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 int ** matrice , i; /* un tableau à deux dimension */ /*allocation d'un tableau de trois tableaux d'entiers */ MALLOC(matrice,int*,3); for ( i = 0 ; i < 3 ; i ++ ) { /* allocation d'un tableau de tableau */ CALLOC(matrice[i],int,3); } /*remplissage d'une matrice diagonale*/ for ( i = 0 ; i < 3 ; i++ ) { matrice[i][i] = 1; } for(i = 0 ; i<3 ; i++) { FREE(matrice[i]); } FREE(matrice);
minfo21 > Merci, mais j'avais déjà résolu le problème, c'était juste pour signaler l'erreur.
Je trouve ce didacticiel de très bonne qualité. Je reste toutefois sur ma fin par rapport aux macros proposées dans la section 4.2 qui cachent des exit() sauvages qui ne me plaisent pas. L'utilisation de tels points de sortie est certes souvent utilisé dans un contexte d'enseignement. Il y a toutefois certainement de meilleures façons de traiter ce type d'erreurs d'allocation dynamiques de mémoire dans la vraie vie. C'est à priori à mon avis une mauvaise pratique et je me demande s'il est opportun en tout cas de cacher ces points de sortie dans une macro. Typiquement, dans une fonction de création de matrice, ma stratégie sera, en cas d'erreur d'allocation, de faire le ménage (i.e. libérer tout ce qui a été allouer), puis de faire remonter l'erreur d'allocation, éventuellement jusqu'à main() qui sera la seule fonction autorisée à quitter l'application.
Avec mes meilleures salutations
Thierry
"The most important thing in the kitchen is the waste paper basket and it needs to be centrally located.", Donald Knuth
"If the only tool you have is a hammer, every problem looks like a nail.", probably Abraham Maslow
FAQ-Python FAQ-C FAQ-C++
+
Bonjour,
Ce tutoriel est très bien fait, car il ma permis de comprendre comment allouer dynamiquement un tableau à deux dimensions, par contre il y a quelque chose que je ne comprend pas. J'ai repris le code du chapitre 1.2 en le modifiant pour l'adapter à ma situation. Il marche très bien mais je comprend pas pourquoi il ne fait pas un segment fault avec ce code :
Ici, l'erreur segment fault arrive pour y = 33756 d'après la sortie du programme :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 //création d'un tableau de 4 tableau de 3 int int **tab , i; tab = (int**) malloc( 4 * sizeof(int)); if( tab == NULL ) { fprintf(stderr,"Allocation impossible"); exit(EXIT_FAILURE); } for( i = 0 ; i < 4 ; i++ ) { tab[i] = (int*) calloc (3 * sizeof(int*)); if( tab[i] == NULL ) { fprintf(stderr,"Allocation impossible"); exit(EXIT_FAILURE); } } //Ce code marche, mais quand jessaie d'utiliser par exemple tab[2][100] il n'y a pas de segment fault, alors que le tableau est sensé sarrêter à tab[3][2], enfin je pense //* int x; int y; for(y = 0 ; y < 100000 ; y++) { for(x = 0 ; x < 4 ; x++) { printf("x = %d et y = %d\n", x, y); tab[x][y] = 0; } }//*/
Je me demande si c'est normal et si oui pourquoi, pouvez vous m'expliquez s'il vous plaît ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 x = 2 et y = 33755 x = 3 et y = 33755 x = 0 et y = 33756 x = 1 et y = 33756 x = 2 et y = 33756 x = 3 et y = 33756 Erreur de segmentation
Bonjour,
Faire la remarque me paraît très intéressant. Mais le code par contre est quelque peu lourd car on fait faire à notre programme des copies de mémoire (inutilement), copies dues notamment à l'introduction de la variable temp. On pourrait faire comme suit non ?
Maintenant que nous avons expliqué comment fonctionnait la fonction realloc(), supposons que tout ne se déroule pas correctement. Par exemple il est impossible de réallouer le tableau. La fonction réalloc va donc nous renvoyer le pointeur NULL. Comme nous avons fait une affection de notre ancien tableau alors nous perdons tous ce qui y était (il s'agit en fait d'une fuite mémoire : le contenu de notre ancien tableau existerai toujours physiquement mais il serait impossible d'y accéder). Il faut donc remplacer notre fonction de réallocation par celle ci :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 int * temp; temp = realloc (tabentier, 4 * sizeof(int) ); if ( temp == NULL ) { fprintf(stderr,"Reallocation impossible"); free(tabentier); exit(EXIT_FAILURE); } else { tabentier = temp; }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 if( realloc(tabentier, 4*sizeof(int))== NULL ) { fprintf(stderr,"Reallocation impossible") ; free(tabentier) ; exit(EXIT_FAILURE); } else { // la mémoire nécessaire a bien été réallouée (pour tabentier) }Cette formulation est quelque peu incohérente. J'aurais préféré Voilà donc ce court exemple où nous allouons dynamiquement de la mémoire pour un entier, lui affectons la valeur 3, l'affichons et enfin libérons la mémoire demandée. Mais ce n'est qu'un point de vue après tout.Voilà donc ce cours exemple, qui alloue dynamiquement un entier, lui affecte la valeur 3, l'affiche et enfin le libère.
Par ailleurs, comme l'ont dit les amis, l'utilisation des macros que tu proposes, à mon avis, n'est pas très utile : le programmeur ne doit pas trouver ennuyeux de recopier/coller les mêmes procédures de test. Mais là aussi, il ne s'agit que de mon point de vue.
Ma conclusion : J'aime bien ton article. On voit que c'est le fruit d'un travail sérieux et pertinant. Bonne journée à vous !
Des espaces mal places.Les fonctions qui seront décrites dans cet article appartiennent toutes au fichier d'en-tête suivant :stdlib.h .Il
Petites fautes de frappe ici :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 /* le type complexe */ struct complexe { double Re; double Im; }; /* une défition plus courte pour la structure */ typedef struct complexe Complexe; Complexe * c; c = malloc(sizeof(Complexe)); if ( c == NULL ) { fprintf(stderr,"Allocation Impossible"); exit(EXIT_FAILURE); }Apres ", passons à un exemple un peu plus évolué", je pense qu'il aurait mieux valu un point et non une virgule, ca rendrait la lecture plus agreable.Maintenant, passons à un exemple un peu plus évolué, supposons que nous voulions allouer un tableau de 3 nombres complexes.
Je trouve ca horrible a lire. Comme ca ce serait pas mieux :Nous allons voir comment avec la fonction malloc nous pouvons résoudre ce problème.
Nous allons voir comment résoudre ce problème avec la fonction malloc.
Il manque un espace a la fin de la premiere phrase.C'est donc un pointeur vers le premier élément du tableau qui est renvoyé.Ceci est donc conforme avec le fait qu'un tableau est égal (en terme de pointeur) au premier élément du tableau. (ie : tab == tab[0] )
Il manque encore un espace.Pour accéder à la deuxième case du tableau, il suffit d'ajouter la taille d'un bloc à l'adresse du premier élément(obtenue avec sizeof).
Comme ca ce serait pas mieux ?On peut également utiliser une autre méthode, utiliser l'arithmétique des pointeurs.
On peut également utiliser une autre méthode : l'arithmétique des pointeurs.
Je pensais qu'on disait developpeur et non programmeur mais la je suis pas sur de mon coup.Ce qui facilite grandement le travail du programmeur, et permet de ne pas faire de différence à l'utilisation entre tableaux dynamiques et tableaux statiques.
J'aurais mis un point juste apres tableau. Comme ceci :Revenons un peu sur l'allocation de notre tableau, comme nous l'avons vu précédemment,
Revenons un peu sur l'allocation de notre tableau. Comme nous l'avons vu précédemment,Une majuscule a ete oubliee.La fonction alloue n blocs de taille t. elle est donc presque équivalente à l'appel suivant :
Il manque un 's' a allouee.La seule différence réside dans le contenu des cases qui sont allouée.
Bizarre cette phrase, nan ?Si ce n'est ce point de détail
Il manque un 's' a entier.Voici donc comment on alloue un tableau de trois tableau de trois entier.
Il manque un 't' a pourrai.On utilise la fonction malloc() ici mais on pourrai utiliser calloc(), c'est juste pour vous montrer que l'on peut utiliser les deux.
Il manque de la ponctuation la.Passons maintenant à la dernière fonction d'allocation : la fonction realloc() voici sa signature :
J'aurais mis un point juste avant "celle ci realloue". Et j'aurais ecrit "celle-ci" et non pas "celle ci". Mais comme ca revient souvent ca doit pas etre genant.Juste après intervient la fonction realloc(), celle ci réalloue le bloc mémoire contenant notre tableau en changeant sa taille.
Il y a une virgule en trop.Cet exemple montre bien que la fonction réalloc recopie notre tableau, et modifie sa taille .
Il manque un 't' a existerai.le contenu de notre ancien tableau existerai toujours physiquement mais il serait impossible d'y accéder)
Je me suis arrete la mais je pense qu'une relecture serait necessaire.
@hantoine: Tu alloues un tableau de 4*3, tab[2][100] n'existe donc pas et tenter d'y accéder est un comportement indéfini.
Non, car realloc() peut retourner une valeur qui n'est ni tabentier, ni NULL.
Si tu veux une version "plus simple", regarde plutôt prendre le pointeur par adresse.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Oui en effet : je viens de relire le manuel et j'ai pu y lire
On part donc de l'ypothèse que tu as raison et nous considérons donc le code suivant :par le manuel : [...] realloc() renvoie un pointeur sur la mémoire nouvellement allouée, qui est correctement alignée pour n’importe quel type de variable, et qui peut être différent de ptr, ou NULL si la demande échoue. [...]
Imagines que realloc retourne une valeur différente de NULL et de tabentier. Quel intérêt ont donc les instructions du else (dans le code précité) ? Je veux parler surtout de l'instruction tabentier = temp; (vu que temp ne vaut ni NULL, ni tabentier).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 int * temp; temp = realloc (tabentier, 4 * sizeof(int) ); if ( temp == NULL ) { fprintf(stderr,"Reallocation impossible"); free(tabentier); exit(EXIT_FAILURE); } else { tabentier = temp; }
S'il ne vaut ni NULL ni tabentier, alors c'est la nouvelle adresse et il faut peut-être la sauvegarder ?
De plus, la "lenteur" induite par la copie d'une variable temporaire est probablement complètement négligeable devant une réallocation.
Si realloc() ne retourne ni NULL ni tabentier, ça veut dire:
- Le pointeur retourné pointe sur le buffer voulu,
- tabentier n'est plus un pointeur valide (realloc() a fait un free() dessus).
En gros, realloc() a du retourner un nouveau tableau car il n'a pas pu l'agrandir sur place (probablement parce qu'il y avait quelque chose d'alloué derrière).
Il faut donc utiliser la nouvelle valeur.
En fait, dans le cas où realloc() marcherait systématiquement, on pourrait faire systématiquement tabentier = realloc(tabentier, nouvelle_taille);. Mais quand realloc() échoue, cette ligne devient mauvaise car le tableau n'a pas été désalloué!
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Humm je suis quelque peu confus là. Je voudrais donc savoir. Dans le texte suivant (tiré du manuel)
de quel ptr parle-t-il ? Est-ce ptr avant reallocation ? Ou est-ce ptr après reallocation ? Il faut surtout noter que je reste toujours inconvaincu. Mais je veux bien vous rassurer que mon but n'est pas de vous importuner, chers internautes. La réponse de medinoc, bien qu'intéressante, ne me fait pas me rendre compte de la différence entre les deux codes suivantspar le manuel : [...] realloc() renvoie un pointeur sur la mémoire nouvellement allouée, qui est correctement alignée pour n’importe quel type de variable, et qui peut être différent de ptr, ou NULL si la demande échoue. [...]
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 int * temp; temp = realloc (tabentier, 4 * sizeof(int) ); if ( temp == NULL ) { fprintf(stderr,"Reallocation impossible"); free(tabentier); exit(EXIT_FAILURE); } else { tabentier = temp; }Y-aurait-il une autre explication ? Ou c'est juste une façon de faire utilisée par les développeurs professionnels, façon de faire que je devrais peut-être suivre ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 if( realloc(tabentier, 4*sizeof(int))== NULL ) { fprintf(stderr,"Reallocation impossible") ; free(tabentier) ; exit(EXIT_FAILURE); } else { // la mémoire nécessaire a bien été réallouée (pour tabentier) }
Je comprends beaucoup mieux. J'en conclue donc que PRomu@ld avait fait le bon choix et je rectifierais en conséquence cette erreur que je fais souvent dans mes codes avec realloc. Bonne soirée à vous !Par medinoc
Si realloc() ne retourne ni NULL ni tabentier, ça veut dire:
- Le pointeur retourné pointe sur le buffer voulu,
- tabentier n'est plus un pointeur valide (realloc() a fait un free() dessus).
C'est forcément ptr avant réallocation, parce que realloc() ne peut pas modifier ptr.
Si realloc() pouvait modifier ptr, on aurait juste besoin de savoir si elle a réussi ou échoué. C'est ce que fait la version au bout de ce lien.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Ce code
ne peut pas fonctionner car comme le fait très justement remarqué Médinoc
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 if( realloc(tabentier, 4*sizeof(int))== NULL ) { fprintf(stderr,"Reallocation impossible") ; free(tabentier) ; exit(EXIT_FAILURE); } else { // la mémoire nécessaire a bien été réallouée (pour tabentier) }
En effet, il aurait fallu que realloc() prenne en paramètre un pointeur sur pointeur pour pouvoir modifier tabentier. Par conséquent, tu passes une adresse à realloc() via ce pointeur et il te renvoie une nouvelle adresse que tu stockes dans la variable que tu veux (le plus logique étant dans tabentier mais ce n'est pas obligatoire).realloc() ne peut pas modifier ptr
PS : je ne sais pas si ça a été dit clairement mais il est bien sûr possible que le retour de realloc() soit égal à tabentier. Cela arrive s'il y avait de la place "à suivre" dans la mémoire.
Tout d'abord merci de m'avoir répondu.
Justement ce que je ne comprend pas c'est que quand le programme tente d'accéder à tab[2][100], il y arrive, et parviens même à modifier la valeur de tab[2][100] sans que le programme ne s’arrête avec un message : erreur de segmentation. Peux tu préciser ce que tu veux dire par "un comportement indéfini".
Ça veut dire qu'absolument n'importe quoi peut arriver, y compris, selon la légende, faire sortir des démons de ton nez.
Ça peut planter tout de suite car tu tapes sur une page mémoire (typiquement par tranches de 4ko ou 8ko) non-allouée, ça peut planter plus tard parce que tu as corrompu un truc, ça peut ne jamais planter, ça peut "planter plus tard" après ta prochaine modif de code dans 20 ans, ça peut planter sur la prochaine version de Windows, etc.
SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.
"Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
Apparently everyone. -- Raymond Chen.
Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.
Ah ok je comprend, merci beaucoup pour tes explications.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager