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,

7 réponses

1 2 3 4 5
Avatar
Benoit Izac
Dans le message , le 22/05/2010 à 20:22, j'ai
écrit :

Si tu veux allouer tes lignes à la volée, c'est possible (non testé) :



J'aurais dû tester, copie revue et compilable :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LINE 2048

int
main(void)
{
char **toutes_les_lignes = NULL;
char une_ligne[MAX_LINE];
unsigned nombre_de_lignes = 0;
unsigned i;

/* lecture */
while (fgets(une_ligne, MAX_LINE, stdin)) {
nombre_de_lignes++;

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

/* allocation pour stocker 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);
}

/* affichage */
for (i = 0; i < nombre_de_lignes; i++)
printf("%s", toutes_les_lignes[i]);

return 0;
}


--
Benoit Izac
Avatar
Antoine Leca
heron écrivit :
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'initiative du
programmeur. Elle sont créées dans le tas (heap).



Oui.

d'après mes connaissances :

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

int Tableau[50] ;



Non.

Une déclaration statique, c'est normalement :
static int Tableau[50] ;

*SAUF QUE* il y a des formes simplifiées, et d'autre part le mot-clé
static signifie AUSSI « pas-extern », ce qui introduit de possibles
confusions.

À l'intérieur d'une fonction, il ne peut pas y avoir de doute, static
signifie statique (il n'est pas possible d'avoir une liaison externe
pour une définition d'objet).

À l'extérieur d'une fonction, en portée globale, static signifie aussi
«pas-extern» (liaison interne), donc si c'est ce que tu veux c'est bon
tel quel ; si tu veux que l'objet soit visible depuis d'autres modules,
il faut enlever le static, et dans ce cas-là l'allocation reste statique


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

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



Oui, mais uniquement à l'intérieur d'une fonction : l'allocation
automatique n'a pas de sens pour une variable globale.

La déclaration « char *pTxt[50] » déclare un tableau de 50 pointeurs
(vers des chaînes).


est-ce que c'est équivalent à :

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



Pas du tout.
La première déclaration, celle de Txt, déclare UNE chaîne de 49
caractères au maximum (plus le '' final); la seconde, celle de pTxt,
déclare UN pointeur vers chaîne ou caractère (contrairement aux 50
ci-dessus), et initialise ce pointeur au début de l'objet Txt.


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.



Bin non.

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



Absolument pas, cela n'a rien à voir. De fait, sur une machine actuelle
et avec un compilateur normal, le temps d'accès aux objets locaux
automatiques sur la pile ou au tas est exactement le même ; et le
compilateur est capable de repérer les objets entiers ou pointeurs
souvent utilisés (qu'ils soient statiques ou sur la pile ou sur le tas)
et les mets en registre tout seul (et c'est le fait d'être en registre
qui est effectivement plus rapide).

De toute manière, l'allocation ne concerne QUE la réservation d'espace
pour l'objet, et est indépendante de l'utilisation qui est faite
ultérieurement de l'objet.


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



Mauvaise conclusion.
NULL correspond à l'absence d'objet.
malloc(0) correspond en théorie à un objet (inutilisable) de 0 octets.
Le fait que ce soit inutilisable a conduit plusieurs implémentations à
remplacer le résultat inutilisable par NULL, dans le but d'empêcher
ensuite l'utilisation illégale du pointeur ; mais il est d'autres
implémentations qui possèdent le concept d'objet alloué vide, et dans
ces cas-là {m,c,re}alloc(0) renvoie bien un objet valide.


Antoine
Avatar
Antoine Leca
heron écrivit :
char *Txt[50] ; /* réservation d'un tableau de 50 lignes ou 50
chaînes? */



Non : réservation (sans initialisation) d'un tableau de 50 pointeurs ;
utilisation normale : pour stocker un index (une table des matières) ;
l'espace pour les chaînes indicées n'est PAS réservé.

char LongeurLigne[100] ; /* longueur d'une ligne */
...
for ( i = 0 ; i < 50 ; i++ ) {
fgets(Txt, 50, stdin) ;



Non : Txt contient des pointeurs, pas des caractères.

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



strlen(LongueurLigne) n'a de sens QUE si LongueurLigne a été
préalablement remplie avec une chaîne (dont on s'est assuré qu'elle
était bien terminée par '', ne PAS utiliser strncpy() à cet effet)

Sinon oui, c'est bien la manière normale d'initialiser un index.


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] ;



Oui.

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



Hum, pas vraiment. En fait dans un programme classique de gestion d'un
tas de lignes de texte, tu as deux inconnues :
- la longueur (maximum) des lignes ;
- le nombre de lignes.

Dans la méthode
: char Txt[50][100] ;
tu fixes les deux inconnues (49 et 100) : avantage, il n'y a plus besoin
de pointeurs, d'allocation dynamique etc. ; inconvénient, c'est moins
souple, le jour où une des deux limites est franchie il faut recompiler
le programme.

Dans la méthode
: char *Txt[50]
tu fixes la seconde inconnues (50 lignes), mais tu laisses la
possibilité de gérer des lignes arbitrairement longues, dans la limite
de la mémoire disponible ; l'entrée des lignes de taille arbitraire
n'est pas résolue, il faut programmer cela (normalement avec des fgets
et des realloc, cf. le début de ce fil).

Symétriquement, tu peux décider d'avoir des lignes de longueur limitée
mais en nombre illimité : un exemple peut être de lire des cartes
perforées. La déclaration clé sera
#define TAILLE_CARTE 80
/*...*/
char (*Txt)[TAILLE_CARTE+1];
Il faudra faire un malloc() au début du programme pour une valeur
« normale » du nombre de cartes ; s'il y a plus de cartes à lire, il
faudra utiliser realloc() pour agrandir l'index. Par contre, la lecture
de chaque carte reste facile, avec un simple
if( !fgets(Txt[i], TAILLE_CARTE+1, flux_lecteur_cartes) )
abandon("erreur de lecture");
Évidemment, le jour où le lecteur de cartes est mis de côté et remplacé
par autre chose, il faudra revoir ce programme...

Et si tu veux la totale, où aucune inconnue n'est figée, Benoît en a
donné le détail.


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



Personnellement je n'aime pas faire l'hypothèse que realloc(NULL, se
comporte comme malloc() : je sais bien que la plupart des
implémentations sont OK, mais je trouve cela dangereux car on ne voit
pas clairement la logique du programme : on redimensionne la chaîne,
mais où se trouve le dimensionnement initial ?

Cela étant, il s'agit de style, ce n'est pas aussi important.


Antoine
Avatar
candide
-ed- a écrit :

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.



C'est écrit dans la Norme, depuis ... 20 ans.
Avatar
espie
In article <4c02f2f5$0$21469$,
candide wrote:
-ed- a écrit :

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.



C'est écrit dans la Norme, depuis ... 20 ans.



Pour malloc(0) ? relis attentivement...
Avatar
candide
Marc Espie a écrit :

Pour malloc(0) ? relis attentivement...



Si, si. Ci-dessous le texte verbatim de la Norme C90 (extrait du livre
de Plauger, "The Standard C Library") :


7.10.3 Memory management functions

The order and contiguity of storage allocated by successive calls to the
calloc, malloc, and realloc functions is unspecified.
(...)
If the size of the space requested is zero, the behavior is
implementation defined; the value returned shall be either a null
pointer or a unique pointer.


La formulation de C99 est un peu plus précise que le "unique pointer".
Avatar
Jean-Marc Bourguet
candide writes:

Marc Espie a écrit :

> Pour malloc(0) ? relis attentivement...

Si, si. Ci-dessous le texte verbatim de la Norme C90 (extrait du livre de
Plauger, "The Standard C Library") :



Marc le sait. C'est ta reponse qui laissait croire que la norme imposait
l'equivalence entre

ptr = NULL;

et

ptr = malloc(0);

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
1 2 3 4 5