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
espie
In article ,
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.



C'est faisable, mais personne ne le fait parce que ca n'est pas efficace.
realloc a un cout non nul, du coup, on en fait moins.

Le classique, c'est de doubler la taille du tampon a chaque fois.
Avatar
heron
Pourquoi ce code ne renvoie pas le texte saisie?

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

int main(void)
{
char* chaine = 0 ;
char c ;
int i = 0 ;

printf("Tapez une phrase :") ;

while ( (c = getchar() ) && c != EOF )
{
++i ;
chaine = realloc(chaine, (i+1) * sizeof(char) ) ;
chaine[i-1] = c ;
chaine[i] = 0 ;
}

printf("nVous avez entre : '%s'n", chaine) ;
free(chaine) ;

return 0 ;
}

simple question : char* chaine et char *chaine c'est différent?
Avatar
heron
Ce que je souhaiterais avec des caractères :

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

int main(void)
{
int input, n ;
int cnt = 0 ;
int *numbers = NULL ;

do
{
printf("nEnter an integer value (enter 0 to stop) : ") ;
scanf("%d", &input) ;
cnt++ ;
numbers = (int *) realloc (numbers, cnt * sizeof(int) ) ;

if ( numbers == NULL )
{
puts("Error (re)allocating memory") ;
exit(1) ;
}

numbers[cnt-1] = input ;

} while ( input != 0 ) ;

printf("nNumbers entered : ") ;

for ( n = 0 ; n < cnt-1 ; n++ )
printf("n%d ", numbers[n]) ;

printf("nn") ;
return 0 ;
}
Avatar
Eric Dorino
On Fri, 14 May 2010 17:08:03 -0700, heron wrote:

Bonjour

Pourquoi ce code ne renvoie pas le texte saisie?



Il le renvoie lorsque une fin de fichier est lue.


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

int main(void)
{
char* chaine = 0 ;
char c ;
int i = 0 ;

printf("Tapez une phrase :") ;

while ( (c = getchar() ) && c != EOF ) {



while ( (c = getchar() ) && c != EOF && c != 'n' ) {
termine la boucle en fin de ligne et affiche le texte.

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


[i-1] = c ;
chaine[i] = 0 ;
}

printf("nVous avez entre : '%s'n", chaine) ; free(chaine) ;

return 0 ;
}

simple question : char* chaine et char *chaine c'est différent?



De le même manière que "bonnet blanc" et "blanc bonnet" sont
différents :-)
Pour le compilateur: non.

--
Eric
Avatar
heron
>    char c ;

>    while ( (c = getchar() ) &&  c != EOF ) {

        while ( (c = getchar() ) &&  c != EOF  && c != 'n' ) {
termine la boucle en fin de ligne et affiche le texte.

> simple question : char* chaine et char *chaine c'est différent?

De le même manière que "bonnet blanc" et "blanc bonnet" sont
différents :-)
Pour le compilateur: non.

--
Eric



Il me semblait que c devait être déclaré comme int pour agir dans le
buffer avec
while ( c = getchar() != 'n' && c != EOF )
;
je pense que c'est parce que EOF renvoie -1 ...

Merci de l'info pour char* et char *
Avatar
Eric Dorino
On Fri, 14 May 2010 18:19:07 -0700, heron wrote:


Il me semblait que c devait être déclaré comme int pour agir dans le
buffer avec



Effectivement.

while ( c = getchar() != 'n' && c != EOF )
;



Attention à la priorité des opérateurs: vous voulez certainement
while ( ( c = getchar() ) != 'n' && c != EOF )

je pense que c'est parce que EOF renvoie -1 ...



Plus exactement, getchar() renvoie un int.


Merci de l'info pour char* et char *



--
Eric
Avatar
espie
Tant qu'a expliquer... meme si c'est de la FAQ.

Le type char est exactement assez grand pour contenir tous les codes de
caracteres.

Du coup, la fin de fichier ne "rentre" pas: c'est un code en plus (et sur
beaucoup d'OS, il n'y a pas de code specifique stocke sur le disque pour
indiquer la fin de fichier, c'est la taille du fichier qui est marquee "quelque
part" dans le repertoire).

En C, on utilise simplement un type un peu plus grand, a savoir int, qui
permet:
- de stocker tous les codes de caracteres sans perte
- de rajouter un code supplementaire, qu'on appelle EOF.

Attention: EOF vaut souvent -1, mais je ne crois pas que ca soit meme
rendu oblige par la norme. Suffit que ca soit une valeur distincte de
tous les codes de caracteres (et c'est le plus souvent -1 parce que sinon du
code mal ecrit va peter, et vous me croirez ma bonne dame, mais du code mal
ecrit il y en a, et souvent les gens font passer la compatibilite avant le
reste...)

Il y a pas mal de details scabreux, mais au final, cote typage:

int char_ou_eof = fgetc(f);
if (char_ou_eof != EOF) {
char c = char_ou_eof;
}

doit avoir l'effet attendu sur tous les OS: lire "quelque chose" d'un fichier,
reperer un EOF, et stocker ce quelque chose sous forme de caractere si on n'est
pas arrive en fin de fichier.
Avatar
Jean-Marc Bourguet
(Marc Espie) writes:

Attention: EOF vaut souvent -1, mais je ne crois pas que ca soit meme
rendu oblige par la norme.



Expression constante entière négative et je n'ai pas l'impression que POSIX
impose plus.

A+

--
Jean-Marc
FAQ de fclc: http://www.levenez.com/lang/c/faq
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Avatar
heron
Pour la saisie avec allocation dynamique de caractères avec getchar()
comme dans le code plus haut ou de nombres entiers avec scanf, je
trouve que ça rend les choses plus simples. Est-ce que pour la saisie
d'une chaîne de caractère, on peut procéder avec fgets au lieu de
getchar() ? Jusque là, fgets était le plus souvent employé.

Mais pour reprendre Marc plus haut

C'est faisable, mais personne ne le fait parce que ca n'est pas efficace.
realloc a un cout non nul, du coup, on en fait moins.



L'allocation dynamique dans la saisie d'une chaîne de caractères de
longeur variable ou d'entier avec scanf n'est pas efficace dans le
sens ou il y a moins de sécurité ?

Le classique, c'est de doubler la taille du tampon a chaque fois.



Est-ce que c'est parce qu'un dépassement de buffer peut se produire
avec l'allocation dynamique. Je ne vois pas très bien comment car
getchar() ne lit qu'un seul caractère à chaque appel. Dans le premier
post, je pensais à fgets, je pense que c'est pour fgets que ce n'est
pas efficace. Je n'ai pas réfléchi à comment y arriver. Mais est-ce
que ce n'est pas efficace parce le code est trop compliqué ou parce
qu'il y a moins de sécurité?
Avatar
espie
In article ,
heron wrote:
L'allocation dynamique dans la saisie d'une chaîne de caractères de
longeur variable ou d'entier avec scanf n'est pas efficace dans le
sens ou il y a moins de sécurité ?



Non, je parle d'efficacite au sens algorithmique.

Le classique, c'est de doubler la taille du tampon a chaque fois.



Est-ce que c'est parce qu'un dépassement de buffer peut se produire
avec l'allocation dynamique. Je ne vois pas très bien comment car
getchar() ne lit qu'un seul caractère à chaque appel. Dans le premier
post, je pensais à fgets, je pense que c'est pour fgets que ce n'est
pas efficace. Je n'ai pas réfléchi à comment y arriver. Mais est-ce
que ce n'est pas efficace parce le code est trop compliqué ou parce
qu'il y a moins de sécurité?



Non, aucun rapport avec la securite.

ce dont je parle, c'est les tampons de taille variable.

schematiquement, et sans controle d'erreur (tous les malloc doivent etre
verifies, en general).

T *t;
size_t size; /* taille utilisee */
size_t capacity; /* taille disponible */

void
init()
{
capacity = N; /* a choisir */
t = malloc(sizeof(T) * N);
size = 0;
}

void
add_element(T e)
{
if (size > capacity)
grow_array();
t[size++] = e;
}

void grow_array()
{
capacity *= 2;
t = realloc(t, sizeof(T) * capacity);
}


(ce que tu proposes, c'est de faire capacity += constante dans grow_array).

Analyse classique algorithmique:
si tu fais croitre la capacite de +N a chaque fois, tu vas gaspiller la place
de N/2 elements en moyenne, et tu vas faire size/N reallocations.

Si tu fais croitre la capacite du tableau * 2 a chaque fois, tu vas gaspiller
la place de 1/4 size elements en moyenne, et tu vas faire log2(size)
reallocations.


On prefere le 2e schema dans l'enorme majorite des applications. Sur la
plupart des OS, le plus souvent, il n'y a "pas de place" apres ton tableau,
et realloc revient donc a allouer un nouveau tableau et tout copier... en
general, la place memoire coute peu cher, mais la bande passante vers la
memoire domine largement le temps d'execution: on s'en fout de gaspiller un
peu, mais on veut faire le moins possible de copies inutiles.

Pour des chaines de caracteres, un idiome classique, c'est d'avoir un
tampon global qui va croitre avec l'idiome ci-dessus, et une fois lue une
chaine qui t'interesse, en general, tu ne stockes que les bouts qui
t'interessent, donc soit tu parses au vol, soit tu fais du strdup() a partir
du tampon... -> au final tu gaspilles un peu de place dans un seul tampon,
tu as tes donnees qui sont copiees au plus juste, et ca marche tres bien...
1 2 3 4 5