Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

comprendre malloc et realloc

47 réponses
Avatar
heron
Bonjour

Je d=E9bute avec le C depuis quelques mois =E0 mon rhytme. J'arrive au
chapitre de l'allocation dynamique. J'ai abord=E9 les tableaux uni et
multidimensionnels, les pointeurs, les fonctions.

En fait, je comprends l'utilit=E9 de malloc dans le sens ou la
r=E9servation de la m=E9moire pour un tableau peut se faire en cours
d'ex=E9cution...

Pour la saisie de texte, je voudrais savoir s'il serait possible de
saisir une cha=EEne de caract=E8re variable avec fgets ? Je pense beaucoup
=E0 utiliser realloc.

Par exemple : je r=E9serve de la m=E9moire pour un pointeur vers une
adresse de type caract=E8re, pour 1 ou 2 caract=E8res et =E0 je demande =E0
l'utilisateur de saisir du texte. A chaque saisie, je r=E9serve la place
pour un caract=E8re suppl=E9mentaire en m=E9moire avec realloc, c'est
faisable? Bizarement, je ne trouve aucun code qui correspond =E0 ce que
je recherche sur le net.

Ca ressemble =E0 peu pr=E8s, mais =E7a ne marche pas :
http://www.cppfrance.com/forum/sujet-RECUERATION-CHAINE-CARACTERE-TAILLE-VA=
RIABLE-STDIN_1405662.aspx

Autre question sans lien avec la premi=E8re :
est-ce que =E9crire char* chaine =3D 0 et char *chaine =3D 0 revient =E0 la
m=EAme chose?

Merci,

10 réponses

1 2 3 4 5
Avatar
-ed-
On 15 mai, 00:21, heron wrote:

Par exemple : je réserve de la mémoire pour un pointeur vers une
adresse de type caractère, pour 1 ou 2 caractères et à je demande à
l'utilisateur de saisir du texte. A chaque saisie, je réserve la place
pour un caractère supplémentaire en mémoire avec realloc, c'est
faisable? Bizarement, je ne trouve aucun code qui correspond à ce que
je recherche sur le net.



http://www.bien-programmer.fr/clib.htm
Modules IO + FSTR
Fonction get_line()

Autre question sans lien avec la première :
est-ce que écrire char* chaine = 0 et char *chaine = 0 revient à la
même chose?



Oui. La position et le nombre d'espaces ne sont pas significatifs en
C.
Avatar
-ed-
On 15 mai, 02:08, heron wrote:
Pourquoi ce code ne renvoie pas le texte saisie?


Ce code donne

Tapez une phrase :Hello world
^Z

Vous avez entre : 'Hello world
'

Process returned 0 (0x0) execution time : 12.058 s
Press any key to continue.


Sous Windows. Qu'est-ce qui ne va pas ?
Avatar
heron
On May 20, 5:41 pm, -ed- wrote:
On 15 mai, 02:08, heron wrote:> Pourquoi ce code n e renvoie pas le texte saisie?

Ce code donne

Tapez une phrase :Hello world
^Z

Vous avez entre : 'Hello world
'

Process returned 0 (0x0)   execution time : 12.058 s
Press any key to continue.

Sous Windows. Qu'est-ce qui ne va pas ?



'n' (pour le 1er code du 2nd ou 3ème post, page précédente) solution
avec :
while ( (c = getchar() ) && c != EOF && c != 'n' )
le tout : sous linux
Avatar
heron
On May 19, 10:45 am, Samuel DEVULDER
com> wrote:
Antoine Leca a écrit :

> Attention, cela fait _ou défait_ la place pour un seul entier _tout
> court_ ; donc si avant le realloc() numbers était un pointeur vers
> l'espace pour dix entiers, après le realloc tu as _perdu_ les neuf
> derniers, d'accord ?
> ../..

> Idem, pas deux _autres_ mais deux _en tout_.

Arf.. Tres bonne remarque, j'avais même pas vu que Heron pouvait penser
que realloc travaillait en relatif "augmentait" ou "diminuais" la zone
mémoire du pointeur. Ca ne m'etait jammais venu à l'esprit que quelqu 'un
imagine cela (le manpage est clair au sujet de realloc).



Excuse mon comportement irrationnel, mais les cours que j'ai pu lire
disent à peu près ceci :

realloc modifie la taille d'une zone déjà allouée.
"pointeur" est l'adresse de la zone à modifier
realloc renvoie la nouvelle adresse de la zone, nul si échec.
Si la "taille" est supérieure à l'ancienne, il y a conservation des
données,
quitte à recopier ces dernières.
• Si la "taille" est inférieure à l'ancienne, il y a conservation des
données,
jusqu'à la nouvelle taille.

©Xavier Jeannin 2000-2005

The realloc() function changes the size of a block of memory that was
previously allocated with malloc() or calloc(). The function prototype
is

void *realloc(void *ptr, size_t size);
The ptr argument is a pointer to the original block of memory. The new
size, in bytes, is specified by size. There are several possible
outcomes with realloc():

If sufficient space exists to expand the memory block pointed to by
ptr, the additional memory is allocated and the function returns ptr.
If sufficient space does not exist to expand the current block in its
current location, a new block of the size for size is allocated, and
existing data is copied from the old block to the beginning of the new
block. The old block is freed, and the function returns a pointer to
the new block.
If the ptr argument is NULL, the function acts like malloc(),
allocating a block of size bytes and returning a pointer to it.
If the argument size is 0, the memory that ptr points to is freed, and
the function returns NULL.
If memory is insufficient for the reallocation (either expanding the
old block or allocating a new one), the function returns NULL, and the
original block is unchanged.

trouvé sur http://www.java-samples.com

A vrai dire, il me reste beaucoup de chose à comprendre au niveau de
la gestion de la mémoire.

Heron: la taille passée à realloc() est une taille absolue (comme
malloc), pas relative! C'est super important à comprendre!



Avec les explications et ce que vous dites, je ne suis pas sûre de
comprendre ce que vous voulez dire par taille absolue

sam (Tapons ou Héron? Héron petit, pas Tapons :) )



Héron maladroit
Avatar
-ed-
On 20 mai, 22:43, heron wrote:

> Heron: la taille passée à realloc() est une taille absolue (comme
> malloc), pas relative! C'est super important à comprendre!

Avec les explications et ce que vous dites, je ne suis pas sûre de
comprendre ce que vous voulez dire par taille absolue



La taille réelle. Si tu as un bloc de 100 bytes que tu veux augmenter
à 300 (cçd 200 de plus), tu passes en paramètre de realloc() 300 (l a
taille réelle demandée ou 'absolue') et non 200 (la supposée taille
supplémentaire ou 'relative' : 200 de plus)
Avatar
heron
On May 21, 7:24 am, -ed- wrote:
On 20 mai, 22:43, heron wrote:

> > Heron: la taille passée à realloc() est une taille absolue (comme
> > malloc), pas relative! C'est super important à comprendre!

> Avec les explications et ce que vous dites, je ne suis pas sûre de
> comprendre ce que vous voulez dire par taille absolue

La taille réelle. Si tu as un bloc de 100 bytes que tu veux augmenter
à 300 (cçd 200 de plus), tu passes en paramètre de realloc()  300  (la
taille réelle demandée ou 'absolue') et non 200 (la supposée taille
supplémentaire ou 'relative' : 200 de plus)



Je comprenais que realloc était un redimensionnement, càd en partant
de dimensions initiales, on déterminait de nouvelles dimensions.

Dans les codes de ce post, il est facile de voir qu'à chaque appel,
realloc est redimensionné et c'est la variable qui précède realloc qu i
contient la nouvelle dimension je pense. C'est visible dans les codes
plus haut. cnt * (sizeof(int))...cnt prends une valeur qui permet de
redefinir la nouvelle taille de *numbers ... à ce niveau, je n'ai pas
de difficultés.

La difficulté c'est d'utiliser malloc et realloc, comprendre à quels
moments et pourquoi les utiliser.

Dans le document de Xavier Jeannin, il dit (chapitre gestion
dynamique : classe d'allocation : )

• statique : les données occupent un emplacement défini lors de la
compilation.
• automatique : les données sont créées dynamiquement au fur et à
mesure
de l'exécution du pgrm, dans la pile (stack) .
• dynamique : les données sont créées ou détruites à l'initiati ve du
programmeur. Elle sont créées dans le tas (heap).

d'après mes connaissances :

- déclaration statique d'un tableau d'entier:

int Tableau[50] ;

- déclaration automatique (je ne suis pas très sûre) :

int Tab[50] ;
int *pTab = Tab ;

il y a aussi int *pTab[50] mais je ne suis pas sûre, c'est plus
fréquent de voir :

char *pTxt[50] ;

est-ce que c'est équivalent à :

char Txt[50];
char *pTxt = Txt ;

pour ce qui précède, ça appartient bien à la déclaration automati que ?
qu'est qui est automatique?

- déclaration dynamique (abrégé)

int *pTab = NULL ;
int i = 0 ;
i++ ;
pTab = (int *) realloc(pTab, cnt * sizeof(int) )

L'auteur dit de qui est dynamique que ça se trouve dans le heap ou
tas. Je pensais que tout ce qui était dynamique, malloc, realloc était
toujours dans le stack ou pile. Enfin, j'affirme sans être certain que
les pointeurs se situent toujours sur la pile (stack) car plus rapides
lors de l'exécution.

En fait, on peut lire plus loin dans le document que realloc modifie
la taille d'une zone déjà allouée. Je peux en conclure que :

int *pTab = NULL ;

est équivalent à :

int *pTab ;
pTab = (int *) malloc(0 * sizeof(int) ) ;
Avatar
-ed-
On 22 mai, 02:42, heron wrote:

Je comprenais que realloc était un redimensionnement, càd en partant
de dimensions initiales, on déterminait de nouvelles dimensions.



Attention au terme 'dimension' qui a un sens bien précis quand on
parle de tableau. Disons plutôt qu'on modifie la taille de l'objet
(resize).


Dans les codes de ce post, il est facile de voir qu'à chaque appel,
realloc est redimensionné



Attention à écrire les choses correctement. realloc() est une
fonction. Elle n'est ni 'redimensionnée', ni agrandie ou rétrécie.
C'est la taille de l'objet (la variable) dont l'adresse est passée en
référence qui est modifiée (et l'adresse de la variable aussi,
éventuellement).

et c'est la variable qui précède realloc qui
contient la nouvelle dimension je pense.



Il ne faut pas faire de suppositions douteuses. On apprend pas à coder
en regardant du code écrit, mais en commençant par comprendre le
fonctionnement des fonctions. Pour cela, il faut en lire la définition
qui en explique

- l'interface (identificateur, paramètres, retour)
- le comportement attendu normal et en erreur.

Une bonne référence pour le C standard est la doc POSIX.1 (qui englobe
toutes les fonctions du C standard)

http://www.opengroup.org/onlinepubs/009695399/functions/realloc.html

C'est visible dans les codes
plus haut. cnt * (sizeof(int))...cnt prends une valeur qui permet de
redefinir la nouvelle taille de *numbers ... à ce niveau, je n'ai pas
de difficultés.



As-tu compris que cette valeur est l'adresse de la variable dont la
taille a été modifiée?

La difficulté c'est d'utiliser malloc et realloc, comprendre à quels
moments et pourquoi les utiliser.



C'est une question d'expérience.Quand on découvre un nouvel outil, on
ne voit pas toujours à quoi il sert. Je prône une approche plus
pragmatique, selon l'adage "le besoin crée l'outil".

Par exemple, on veut lire un fichier contenant un nombre quelconque de
lignes au format CSV (Comma Separated Values). Chaque ligne
représente, par exemple, une transaction (date, bénéfiaire, montant).

02/01/2010; Hercule Poirot; 1000
15/01/2010; Miss Marple; 1200
13/02/2010; Hercule Poirot; 1500
etc.

On veut ensuite faire un état du total par bénéficiaires.

Hercule Poirot
.02/01/2010 1000
.13/02/2010 1500
Sous-total........2500

Miss Marple
.15/01/2010 1200
Sous-total........1200

Total.............2700
(j'ai mis des points pour simuler les espaces)

On voit bien qu'il va falloir charger les données en mémoire, puis les
traiter (trire). On sait facilement trier des tableaux (ici, de
structures), donc, charger les données dans un tableau parait une
chose assez évidente, car on peut alors trier les tableau selon
plusieurs critères... (qsort()).

on voit donc qu'il va falloir un tableau dont la taille sera définie
au fur et à mesure de la lecture.

Algorithme déroulé :

1 - on commence avec une taille de 1
2 - on lit la première ligne.
3 - si ce n'est pas la fin, on double la taille : 1 x 2 = 2
4 - on lit la ligne suivante
5 - si ce n'est pas la fin, on lit la ligne suivante
6 - si ce n'est pas la fin, on double la taille : 2 x 2 = 4
etc. jusqu'à ce que tout le tableau soit chargé.

ensuite, on applique les traitements qui vont bien (par exemple tri
par date, puis tri par bénéficiaire). Ensuite, un traitement en
séquence pour gérer les montants, sous-totaux et totaux permet de
sortir l'état correctement.


Dans le document de Xavier Jeannin, il dit (chapitre gestion
dynamique : classe d'allocation : )

• statique : les données occupent un emplacement défini lors de la
  compilation.



On ne définit pas l'emplacement, mais le comportement, c'est à dire
principalement la durée de vie et la portée. Quelque soit la portée
(locale, module, programme), la variable est créée et initialisée (pa r
défaut, à 0) avant l'exécution du programme, et elle disparait une
fois que le programme est terminé.

• automatique : les données sont créées dynamiquement au fur et à
mesure



Je précise "crées et détruites ..." Se méfier du terme 'dynamiqueme nt'
réservé à la création 'volontaire' de données (cas #3). On préf ère le
terme 'automatiquement'..

  de l'exécution du pgrm, dans la pile (stack) .



OK.

• dynamique : les données sont créées ou détruites à l'initia tive du
  programmeur. Elle sont créées dans le tas (heap).



OK.

d'après mes connaissances :

- déclaration statique d'un tableau d'entier:

int Tableau[50] ;



Il y a d'autres possibilités :

Dans un bloc (portée locale)

{
static int Tableau[50] ;
<...>

Hors d'un bloc (portée 'module' ou 'unité de compilation) :

static int Tableau[50] ;

Hors d'un bloc (portée 'globale' ou 'totale') :

int Tableau[50] ;

- déclaration automatique (je ne suis pas très sûre) :

int Tab[50] ;
int *pTab = Tab ;



Non, pas du tout. Une déclaration 'automatique' se fait
obligatoirement dans un bloc, sans le mot clé 'static'.

{
int Tableau[50] ;
<...>


il y a aussi int *pTab[50] mais je ne suis pas sûre, c'est plus
fréquent de voir :



Complètement autre chose.


char *pTxt[50] ;

est-ce que c'est équivalent à :

char Txt[50];
char *pTxt = Txt ;



On apprends pas un langage de programmation en jouant aux devinettes
en regardant du code qu'on ne comprend pas. Il y a des règles de
syntaxe et des définitions bien précises à connaitre. Tout est dans
ton livre de C.

- déclaration dynamique (abrégé)

int *pTab = NULL ;
int i = 0 ;
i++ ;
pTab = (int *) realloc(pTab, cnt * sizeof(int) )




C'est bien compliqué.

int *pTab = NULL ;
int i = 0 ;

i++ ;
pTab = realloc(pTab, cnt * sizeof *pTab);

http://www.bien-programmer.fr/notes.php#malloc
http://www.bien-programmer.fr/notes.php#realloc

et d'une manière générale, beaucoup de réponses à tes questions i ci :

http://www.bien-programmer.fr/notes.php

L'auteur dit de qui est dynamique que ça se trouve dans le heap ou
tas.



Oui.

Je pensais que tout ce qui était dynamique, malloc, realloc était
toujours dans le stack ou pile.



malloc() et realloc() sont des fonctions. Elle ne sont ni dans le tas
ni dans la pile (mais dans la mémoire d'exécution, enfin, peu
importe). Par contre, elle permettent de créer dynamiquement des
objets dans le tas (sur la plupart des implémentations).

La pile (quand elle existe) est faite pour recevoir les variables
automatiques.

Enfin, j'affirme sans être certain que
les pointeurs se situent toujours sur la pile (stack) car plus rapides
lors de l'exécution.



Cette phrase n'a aucun sens. de plus, le C ne définit pas 'plus
rapide' ...

Un pointeur peut être définit en mémoire statique, automatique ou
dynamique. Pareil pour la zone pointée. (on évitera quand même les
combinaisons douteuses, notamment de pointer sur une zone automatique
qui n'existe plus ...). On ne peut pas faire tout et n'importe quoi.
Encore une fois, le C est puissant, subtil, bourré de pièges et
laxiste. Il permet, du moins techniquement, de faire un tas de choses
inavouables ... La plupart sont des bugs !

En fait, on peut lire plus loin dans le document que realloc modifie
la taille d'une zone déjà allouée. Je peux en conclure que :



Éventuellement, oui. Elle peut même recréer ailleurs une zone plus
grande et y recopier les données contenues dans la zone précédente,
mais peu importe, le résultat visible est le même.

int *pTab = NULL ;

 est équivalent à :

int *pTab ;
pTab = (int *) malloc(0 * sizeof(int) ) ;



ou simplement

int *pTab = malloc (0 * sizeof *pTab ) ;

Pour ça, il faudrait être certain que le comportement de malloc(0)
soit bien défini par le C, ce dont je ne suis pas sûr. A vérifier dan s
une doc de référence (et non une doc d'implémentation). Je te laisse
le faire ...
Avatar
Benoit Izac
Bonjour,

le 22/05/2010 à 10:49, -ed- a écrit dans
le message
:

int *pTab = malloc (0 * sizeof *pTab ) ;

Pour ça, il faudrait être certain que le comportement de malloc(0)
soit bien défini par le C, ce dont je ne suis pas sûr. A vérifier dans
une doc de référence (et non une doc d'implémentation). Je te laisse
le faire ...



Selon SUSv3
(<http://www.opengroup.org/onlinepubs/009695399/functions/malloc.html>) :

| void *malloc(size_t size);
|
| If the size of the space requested is 0, the behavior is
| implementation-defined: the value returned shall be either a null
| pointer or a unique pointer.

--
Benoit Izac
Avatar
heron
Alors si je comprends les bases, en fait je ne vais pas à tatons mais
les cours n'abordent pas tout les aspects de la gestion dynamique et à
mon niveau je vois 2 aspects du recours à la gestion dynamique :

1-
char *Txt[50] ; /* réservation d'un tableau de 50 lignes ou 50
chaînes? */
char LongeurLigne[100] ; /* longueur d'une ligne */
...
for ( i = 0 ; i < 50 ; i++ ) {
fgets(Txt, 50, stdin) ;
Txt[i] = (char*) malloc(strlen(LongueurLigne)+1)

if (Txt[i] )
strcpy(Txt[i], LongueurLigne) ; /* l'allocation */
else /* quitter */
...
}
...
Le gain de l'allocation dynamique d'un tableau de 50 lignes pour une
longueur maximale de 100 caractères par ligne serait d'économiser de
l'espace mémoire sur la longeur d'une ligne au lieu de déclarer :

char Txt[50][100] ;

Par contre l'inconvénient d'après moi est qu'on doit avoir une idée d u
nombre de ligne sans quoi on ne peux pas modifier facilement *Txt

2-

Par contre avec :

char *chaine = 0 ;
while...
chaine = realloc(chaine, (i+1) * sizeof(char) ) ;

dans ce cas, on peut mettre en mémoire autant de ligne qu'il y a de
place en mémoire

De ces 2 cas que j'ai pu comprendre après avoir lu de nombreux cours
sur le sujet, le K&R n'est pas très bavard à ce sujet ni d'autres
ouvrage. Les recherches sur Internet font surtout apparaître le 1er
cas.
Le 2ème cas me semble avoir plus d'impact d'après ce qu'on attend de
la gestion dynamique et surtout de la flexibilité sur une déclaration
statique (aucune limite de saisie).

Là encore, je ne suis pas sûre de bien comprendre.
Avatar
Benoit Izac
Bonjour,

le 22/05/2010 à 16:05, heron a écrit dans le message
:

Alors si je comprends les bases, en fait je ne vais pas à tatons mais
les cours n'abordent pas tout les aspects de la gestion dynamique et à
mon niveau je vois 2 aspects du recours à la gestion dynamique :

1-
char *Txt[50] ; /* réservation d'un tableau de 50 lignes ou 50
chaînes? */



50 adresses mémoire pointant vers des éléments de type char.

char LongeurLigne[100] ; /* longueur d'une ligne */



Là tu déclares un tableau de 100 éléments de type char. Le nom parait
très mal choisi, Ligne[100] serait plus judicieux.

...
for ( i = 0 ; i < 50 ; i++ ) {
fgets(Txt, 50, stdin) ;



Ça n'a pas de sens ; je pense que tu voulais faire :
fgets(LongueurLigne, 100, stdin);

Txt[i] = (char*) malloc(strlen(LongueurLigne)+1)



Il n'est pas nécessaire de "caster" malloc().

if (Txt[i] )
strcpy(Txt[i], LongueurLigne) ; /* l'allocation */
else /* quitter */
...
}
...
Le gain de l'allocation dynamique d'un tableau de 50 lignes pour une
longueur maximale de 100 caractères par ligne serait d'économiser de
l'espace mémoire sur la longeur d'une ligne au lieu de déclarer :

char Txt[50][100] ;



Effectivement, si tes lignes ne contiennent que 'n', tu économises
50 x 98 bytes.

Par contre l'inconvénient d'après moi est qu'on doit avoir une idée du
nombre de ligne sans quoi on ne peux pas modifier facilement *Txt



*Txt c'est *Txt[0] soit un char donc je ne comprends pas ta remarque.
Si tu veux allouer tes lignes à la volée, c'est possible (non testé) :

char **toutes_les_lignes = NULL;
char une_ligne[MAX_LINE];
unsigned nombre_de_lignes = 0;

while (1) {
/* lecture de la ligne */
fgets(une_ligne, MAX_LINE, stdin);

if (une_ligne) {
nombre_de_ligne++;

/* allocation pour une ligne en plus */
toutes_les_lignes = realloc(toutes_les_lignes,
nombre_de_ligne * sizeof *toutes_les_lignes);
if (!toutes_les_lignes)
exit(1);

/* allocation pour stocké la ligne */
toutes_les_lignes[nombre_de_lignes - 1] malloc(strlen(une_ligne) + 1);

if (toutes_les_lignes[nombre_de_lignes - 1])
/* copie */
strcpy(toutes_les_lignes[nombre_de_lignes - 1], une_ligne);
} else
break;
}


2-

Par contre avec :

char *chaine = 0 ;
while...
chaine = realloc(chaine, (i+1) * sizeof(char) ) ;



Par définition sizeof char == 1.

dans ce cas, on peut mettre en mémoire autant de ligne qu'il y a de
place en mémoire

De ces 2 cas que j'ai pu comprendre après avoir lu de nombreux cours
sur le sujet, le K&R n'est pas très bavard à ce sujet ni d'autres
ouvrage. Les recherches sur Internet font surtout apparaître le 1er
cas.
Le 2ème cas me semble avoir plus d'impact d'après ce qu'on attend de
la gestion dynamique et surtout de la flexibilité sur une déclaration
statique (aucune limite de saisie).

Là encore, je ne suis pas sûre de bien comprendre.




--
Benoit Izac
1 2 3 4 5