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

Question sur realloc

67 réponses
Avatar
Kevin Denis
Bonjour,

j'ai une question sur realloc:
$ cat testrealloc.c
#include <stdio.h>
#include <stdlib.h>

int main(void){
char * c=NULL;
c=malloc(8);
if (c == NULL) return -1;
c="abcdefgh";
printf("valeur du pointeur: %d\r\n",c);
printf("valeur de c: %s\r\n",c);
c=realloc(c,16);
return 0;
}
$ gcc -Wall -o testrealloc testrealloc.c
testrealloc.c: In function 'main':
testrealloc.c:9: attention : format '%d' expects type 'int', but argument 2
has type 'char *'
$ ./testrealloc
valeur du pointeur: 134514000
valeur de c: abcdefgh
Erreur de segmentation

Ma question est: pourquoi le realloc segfaulte?
Merci
--
Kevin

10 réponses

1 2 3 4 5
Avatar
JKB
Le 03 May 2011 10:06:58 GMT,
Kevin Denis écrivait :
Bonjour,

j'ai une question sur realloc:
$ cat testrealloc.c
#include <stdio.h>
#include <stdlib.h>

int main(void){
char * c=NULL;
c=malloc(8);



c=malloc(9); !

if (c == NULL) return -1;
c="abcdefgh";



Euh... Je ne suis pas bien certain de ce que tu veux faire ici.
"abcdefg" est un pointeur statique. Il n'a pas besoin d'être alloué.

printf("valeur du pointeur: %drn",c);
printf("valeur de c: %srn",c);
c=realloc(c,16);
return 0;
}
$ gcc -Wall -o testrealloc testrealloc.c
testrealloc.c: In function 'main':
testrealloc.c:9: attention : format '%d' expects type 'int', but argument 2
has type 'char *'
$ ./testrealloc
valeur du pointeur: 134514000
valeur de c: abcdefgh
Erreur de segmentation

Ma question est: pourquoi le realloc segfaulte?



Parce que tu tentes de réallouer un pointeur statique alors que tu
as déjà perdu la valeur allouée par malloc().

Merci



JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
JKB
Le Tue, 3 May 2011 10:16:30 +0000 (UTC),
JKB écrivait :
Le 03 May 2011 10:06:58 GMT,
Kevin Denis écrivait :
Bonjour,

j'ai une question sur realloc:
$ cat testrealloc.c
#include <stdio.h>
#include <stdlib.h>

int main(void){
char * c=NULL;
c=malloc(8);



c=malloc(9); !

if (c == NULL) return -1;
c="abcdefgh";



Euh... Je ne suis pas bien certain de ce que tu veux faire ici.
"abcdefg" est un pointeur statique. Il n'a pas besoin d'être alloué.



En fait, c'est un strcpy() qu'il te faut ici.

printf("valeur du pointeur: %drn",c);
printf("valeur de c: %srn",c);
c=realloc(c,16);
return 0;
}
$ gcc -Wall -o testrealloc testrealloc.c
testrealloc.c: In function 'main':
testrealloc.c:9: attention : format '%d' expects type 'int', but argument 2
has type 'char *'
$ ./testrealloc
valeur du pointeur: 134514000
valeur de c: abcdefgh
Erreur de segmentation

Ma question est: pourquoi le realloc segfaulte?



Parce que tu tentes de réallouer un pointeur statique alors que tu
as déjà perdu la valeur allouée par malloc().

Merci



JKB





--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Alexandre Bacquart
On 05/03/2011 12:06 PM, Kevin Denis wrote:
Bonjour,

j'ai une question sur realloc:
$ cat testrealloc.c
#include<stdio.h>
#include<stdlib.h>

int main(void){
char * c=NULL;
c=malloc(8);
if (c == NULL) return -1;
c="abcdefgh";



Ton intention est visiblement de copier la chaîne dans la mémoire
allouée, mais ça ne fonctionne pas ainsi. Ce qu'il se passe ici, c'est
que le pointeur c va pointer sur le début de la chaîne "abcdefgh"
(statique) et non plus sur la mémoire allouée par malloc (qui du coup ne
pourra pas être libérée, ce que d'ailleurs tu ne fais pas, c'est mal).

Pour copier une chaîne, il y a :

strcpy(c, "abcdefgh"); // #include <string.h>

Mais il faut s'assurer d'avoir alloué la taille nécessaire. La taille
d'une chaîne C en mémoire, c'est le nombre de caractères qu'elle
contient + le '' final (donc 9 char dans ton cas). Plus proprement,
ton test devrait être :

const char str[] = "abcdefgh";

On a défini la chaîne à priori pour déterminer la taille de son tampon
avec sizeof :

char *c = malloc(sizeof(str));

On pourrait aussi faire :

char *c = malloc(strlen(str) + 1); // 8 + 1 = 9

Mais c'est moins efficace pour des chaînes statiques, car strlen()
calcule la taille de la chaîne en la parcourant (et ne compte pas le
'' final, c'est pour cette raison que je fais + 1), mais sizeof ne
calcule rien du tout (il retourne la taille du tampon statique de la
chaîne, '' inclus). Ensuite :

strcpy(c, str);

Il est important d'allouer suffisamment pour strcpy() qui ne vérifie
rien du tout. Il copie sans relâche jusqu'à rencontrer le '' final
(qui est aussi copié). Si ça déborde -> comportement indéfini (en bref,
rien ou boum tôt ou tard, mais plus le programme grossit, plus les
chances de boum augmentent).

printf("valeur du pointeur: %drn",c);



Warning: %d c'est pour les int. Pour les pointeurs, il faut utiliser %p.

Au passage, 'n' suffit pour le retour à la ligne, pas besoin d'utiliser
'r' (en sortie de printf(), ça n'aura aucun effet notable).

printf("valeur de c: %srn",c);
c=realloc(c,16);



free(c);

return 0;
}
$ gcc -Wall -o testrealloc testrealloc.c
testrealloc.c: In function 'main':
testrealloc.c:9: attention : format '%d' expects type 'int', but argument 2
has type 'char *'
$ ./testrealloc
valeur du pointeur: 134514000
valeur de c: abcdefgh
Erreur de segmentation

Ma question est: pourquoi le realloc segfaulte?



Parce-que realloc() a besoin d'un pointeur initialisé avec malloc() et
le pointeur c n'a plus cette valeur au moment de l'appel. Elle est
perdue pour ton programme (et free(c) ne fonctionnerait pas, résultat :
fuite mémoire).


--
Alex
Avatar
Kevin Denis
Le 03-05-2011, Alexandre Bacquart a écrit :
c="abcdefgh";



Ton intention est visiblement de copier la chaîne dans la mémoire
allouée, mais ça ne fonctionne pas ainsi. Ce qu'il se passe ici, c'est
que le pointeur c va pointer sur le début de la chaîne "abcdefgh"
(statique) et non plus sur la mémoire allouée par malloc



Ok, vu.

(qui du coup ne
pourra pas être libérée, ce que d'ailleurs tu ne fais pas, c'est mal).



J'avais réduit le programme pour qu'il présente le problème, mais oui,
il faudrait un free à la fin.

Pour copier une chaîne, il y a :

strcpy(c, "abcdefgh"); // #include <string.h>

Mais il faut s'assurer d'avoir alloué la taille nécessaire. La taille
d'une chaîne C en mémoire, c'est le nombre de caractères qu'elle
contient + le '' final (donc 9 char dans ton cas). Plus proprement,
ton test devrait être :

const char str[] = "abcdefgh";

On a défini la chaîne à priori pour déterminer la taille de son tampon
avec sizeof :

char *c = malloc(sizeof(str));

On pourrait aussi faire :

char *c = malloc(strlen(str) + 1); // 8 + 1 = 9

Mais c'est moins efficace pour des chaînes statiques, car strlen()
calcule la taille de la chaîne en la parcourant (et ne compte pas le
'' final, c'est pour cette raison que je fais + 1), mais sizeof ne
calcule rien du tout (il retourne la taille du tampon statique de la
chaîne, '' inclus). Ensuite :

strcpy(c, str);



De manière bien plus rapide et sans doute moins propre, je devrais
pouvoir faire:
c=malloc(9);
strcpy(c,"abcdefgh");
c=realloc(c,17);
strcpy(c,"abcdefghabcdefgh");

Il est important d'allouer suffisamment pour strcpy() qui ne vérifie
rien du tout. Il copie sans relâche jusqu'à rencontrer le '' final
(qui est aussi copié). Si ça déborde -> comportement indéfini (en bref,
rien ou boum tôt ou tard, mais plus le programme grossit, plus les
chances de boum augmentent).



:) C'est écrit explicitement dans le man, j'ai vu. Ceci dit, j'ai testé et
j'ai aussi bien des segfault que des fonctionnements normaux.

printf("valeur du pointeur: %drn",c);



Warning: %d c'est pour les int. Pour les pointeurs, il faut utiliser %p.



Ok.

Au passage, 'n' suffit pour le retour à la ligne, pas besoin d'utiliser
'r' (en sortie de printf(), ça n'aura aucun effet notable).

printf("valeur de c: %srn",c);
c=realloc(c,16);



free(c);

return 0;
}
$ gcc -Wall -o testrealloc testrealloc.c
testrealloc.c: In function 'main':
testrealloc.c:9: attention : format '%d' expects type 'int', but argument 2
has type 'char *'
$ ./testrealloc
valeur du pointeur: 134514000
valeur de c: abcdefgh
Erreur de segmentation

Ma question est: pourquoi le realloc segfaulte?



Parce-que realloc() a besoin d'un pointeur initialisé avec malloc() et
le pointeur c n'a plus cette valeur au moment de l'appel. Elle est
perdue pour ton programme (et free(c) ne fonctionnerait pas, résultat :
fuite mémoire).



C'est très clair, merci
--
Kevin
Avatar
Alexandre Bacquart
On 05/03/2011 02:31 PM, Kevin Denis wrote:
Le 03-05-2011, Alexandre Bacquart a écrit :
const char str[] = "abcdefgh";

On a défini la chaîne à priori pour déterminer la taille de son tampon
avec sizeof :

char *c = malloc(sizeof(str));

On pourrait aussi faire :

char *c = malloc(strlen(str) + 1); // 8 + 1 = 9

Mais c'est moins efficace pour des chaînes statiques, car strlen()
calcule la taille de la chaîne en la parcourant (et ne compte pas le
'' final, c'est pour cette raison que je fais + 1), mais sizeof ne
calcule rien du tout (il retourne la taille du tampon statique de la
chaîne, '' inclus). Ensuite :

strcpy(c, str);



De manière bien plus rapide et sans doute moins propre, je devrais
pouvoir faire:



Et non :

c=malloc(9);
strcpy(c,"abcdefgh");



const char str[] = "abcdefgh";
char *c = malloc(sizeof(str));
strcpy(c, str);

C'est strictement aussi efficace (avec un compilateur ne datant pas de
l'âge de pierre, et encore).

Faire du C efficace, c'est déjà savoir ce qui est statique (ie. connu au
moment de la compilation) et ce qui ne l'est pas. Pour sizeof, cet
opérateur ne retourne que des valeurs statiques. Si tu compares les
codes compilés (et que tu connais un peu l'assembleur), tu verras que
l'argument de malloc() sera 9 dans les deux cas (autrement dit, aucune
fonction sizeof n'est appelée).

Et pour la chaîne :

const char str[] = "abcdefgh";

Ceci définit une chaîne statique, ce qui revient au même que de passer
la chaîne littéralement à strcpy().

Ta méthode est juste plus "rapide à écrire", mais au final, le résultat
est le même. La différence, c'est que ton code sera plus difficile à
maintenir : changer la chaîne nécessite pour le programmeur de
recalculer sa taille pour changer l'argument de malloc(). C'est non
seulement ennuyeux, mais source d'erreurs. Avec ma méthode, le
programmeur change... la chaîne. C'est tout.

Résultat : c'est moins rapide à écrire, mais plus propre, plus sûr et à
terme, plus simple à maintenir.



--
Alex
Avatar
Kevin Denis
Le 03-05-2011, Alexandre Bacquart a écrit :
De manière bien plus rapide et sans doute moins propre, je devrais
pouvoir faire:



Et non :

c=malloc(9);
strcpy(c,"abcdefgh");



const char str[] = "abcdefgh";
char *c = malloc(sizeof(str));
strcpy(c, str);

C'est strictement aussi efficace (avec un compilateur ne datant pas de
l'âge de pierre, et encore).

Faire du C efficace, c'est déjà savoir ce qui est statique (ie. connu au
moment de la compilation) et ce qui ne l'est pas. Pour sizeof, cet
opérateur ne retourne que des valeurs statiques. Si tu compares les
codes compilés (et que tu connais un peu l'assembleur), tu verras que
l'argument de malloc() sera 9 dans les deux cas (autrement dit, aucune
fonction sizeof n'est appelée).

Et pour la chaîne :

const char str[] = "abcdefgh";



J'ai un peu de mal à saisir la différence entre
char * str = "abcdefgh";
et
char str[] = "abcdefgh";

si ce n'est que dans un cas je manipule un pointeur et de l'autre une
chaîne. Dans les deux cas, le malloc a été fait implicitement puisque
9 octets ont été reservés (8 caractères + )?

Ceci définit une chaîne statique, ce qui revient au même que de passer
la chaîne littéralement à strcpy().

Ta méthode est juste plus "rapide à écrire",



Je cherchai juste à bien comprendre l'usage de realloc.

mais au final, le résultat
est le même. La différence, c'est que ton code sera plus difficile à
maintenir : changer la chaîne nécessite pour le programmeur de
recalculer sa taille pour changer l'argument de malloc(). C'est non
seulement ennuyeux, mais source d'erreurs. Avec ma méthode, le
programmeur change... la chaîne. C'est tout.

Résultat : c'est moins rapide à écrire, mais plus propre, plus sûr et à
terme, plus simple à maintenir.



La maintenance de ce programme risque de ne pas être bien longue :)
Ceci dit, je garde en tête les remarques.
--
Kevin
Avatar
Erwan David
Kevin Denis écrivait :

Le 03-05-2011, Alexandre Bacquart a écrit :
De manière bien plus rapide et sans doute moins propre, je devrais
pouvoir faire:



Et non :

c=malloc(9);
strcpy(c,"abcdefgh");



const char str[] = "abcdefgh";
char *c = malloc(sizeof(str));
strcpy(c, str);

C'est strictement aussi efficace (avec un compilateur ne datant pas de
l'âge de pierre, et encore).

Faire du C efficace, c'est déjà savoir ce qui est statique (ie. connu au
moment de la compilation) et ce qui ne l'est pas. Pour sizeof, cet
opérateur ne retourne que des valeurs statiques. Si tu compares les
codes compilés (et que tu connais un peu l'assembleur), tu verras que
l'argument de malloc() sera 9 dans les deux cas (autrement dit, aucune
fonction sizeof n'est appelée).

Et pour la chaîne :

const char str[] = "abcdefgh";



J'ai un peu de mal à saisir la différence entre
char * str = "abcdefgh";
et
char str[] = "abcdefgh";

si ce n'est que dans un cas je manipule un pointeur et de l'autre une
chaîne. Dans les deux cas, le malloc a été fait implicitement puisque
9 octets ont été reservés (8 caractères + )?



char * str1 = "abcdefgh";
char str2[] = "abcdefgh";
sizeof(str1) -> sizeof(char *)
sizeof(str2) -> 9

Il y a une subtile différence entre pointeurs et tableaux.

--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé
Avatar
Tonton Th
On 05/03/2011 03:25 PM, Kevin Denis wrote:

J'ai un peu de mal à saisir la différence entre
char * str = "abcdefgh";
et
char str[] = "abcdefgh";



Il suffit de faire un petit dessin, de bien réfléchir,
et la lumière viendra.

Dans le premier cas, tu as deux intervenants : une chaine
'anonyme' contenant "abcdefgh" et une variable 'pointeur'
contenant l'adresse de la chaine anonyme.

Dans le second cas, tu as exactement la même chaine en
mémoire, mais son adresse est devenue automagiquement
une constante.

Quand tu auras saisi ces mécanismes sous-jacents, tu
auras fait un bon pas pour la compréhension de ce
langage diabolique qu'est le C ;)


--
Ma coiffeuse est formidable - http://sonia.buvette.org/
Avatar
Marc Boyer
Le 03-05-2011, Kevin Denis a écrit :
Le 03-05-2011, Alexandre Bacquart a écrit :
De manière bien plus rapide et sans doute moins propre, je devrais
pouvoir faire:



Et non :

c=malloc(9);
strcpy(c,"abcdefgh");



const char str[] = "abcdefgh";
char *c = malloc(sizeof(str));
strcpy(c, str);

C'est strictement aussi efficace (avec un compilateur ne datant pas de
l'âge de pierre, et encore).

Faire du C efficace, c'est déjà savoir ce qui est statique (ie. connu au
moment de la compilation) et ce qui ne l'est pas. Pour sizeof, cet
opérateur ne retourne que des valeurs statiques. Si tu compares les
codes compilés (et que tu connais un peu l'assembleur), tu verras que
l'argument de malloc() sera 9 dans les deux cas (autrement dit, aucune
fonction sizeof n'est appelée).

Et pour la chaîne :

const char str[] = "abcdefgh";



J'ai un peu de mal à saisir la différence entre
char * str = "abcdefgh";
et
char str[] = "abcdefgh";



Parce que les deux sautent des étapes.

Prenons la version verbeuse, et avec des tableaux d'entier.

int tab[4]={1,2,3,4};
int* ptab= &(tab[0]);

Je déclare un tableau de 4 entiers (tab) que j'initialise avec 4 valeurs, puis
je déclare un pointeur sur le premier élément.

Aller, je commence à simplifier:
int tab[]={1,2,3,4};
int* ptab= tab;
Strictement là même chose, sauf que le compilateur calcule tout seul la taille
de tab, et m'évite un & et un [0].

Passons aux chaines de caractères:
char tab[]="123";
char* ptab= tab;
J'ai toujours mes deux variables, ok ?

On peut maintenant faire tes deux écritures.
char str[] = "abcdefgh";
c'est pareil que
char tab[] = "abcdefgh";
j'ai défini une variable tab, un tableau de 10 char, initialisé avec des valeurs.
Je peux écrire dedans.

Par contre, écrire
char* str= "abcdefg";
c'est équivalent à écrire
char tmp[]= "abcdefg";
char* str= tmp;
mais sans réserver l'identifiant tmp (c'est une variable anonyme), et en plus,
on ne peut pas écrire dans ce tableau.

Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
Avatar
espie
In article ,
Kevin Denis wrote:
Le 03-05-2011, Alexandre Bacquart a écrit :
De manière bien plus rapide et sans doute moins propre, je devrais
pouvoir faire:



Et non :

c=malloc(9);
strcpy(c,"abcdefgh");



const char str[] = "abcdefgh";
char *c = malloc(sizeof(str));
strcpy(c, str);

C'est strictement aussi efficace (avec un compilateur ne datant pas de
l'âge de pierre, et encore).

Faire du C efficace, c'est déjà savoir ce qui est statique (ie. connu au
moment de la compilation) et ce qui ne l'est pas. Pour sizeof, cet
opérateur ne retourne que des valeurs statiques. Si tu compares les
codes compilés (et que tu connais un peu l'assembleur), tu verras que
l'argument de malloc() sera 9 dans les deux cas (autrement dit, aucune
fonction sizeof n'est appelée).

Et pour la chaîne :

const char str[] = "abcdefgh";



J'ai un peu de mal à saisir la différence entre
char * str = "abcdefgh";
et
char str[] = "abcdefgh";

si ce n'est que dans un cas je manipule un pointeur et de l'autre une
chaîne. Dans les deux cas, le malloc a été fait implicitement puisque
9 octets ont été reservés (8 caractères + )?



Il y a une autre ENORME difference entre les deux.

Dans le premier cas:
char *str = "abcdefgh";
tu prends une chaine de caractere et tu stockes son adresse dans str.

Or, les chaines de caracteres sont des constantes. Par exemple, soit
le mini programme (non conforme) suivant:
#include <stdio.h>
int main()
{
char *str = "abcdefgh";
str[2] = 'e';
printf("%sn", str);
return 0;
}

chez moi, si je le compile et l'execute, il affiche...
abcdefgh

Pourquoi ? parce que la chaine est stockee avec les donnees en RO, et donc
que la modif, str[2], est inoperante.

(c'est non conforme car la ligne str[2] = 'e'; n'a pas de comportement defini)

Si je remplace par
char str[] = "abcdefgh";
ca va afficher
abedefgh

c'est tres different: je cree un tableau de caracteres que j'initialise
avec les valeurs de la chaine "abcdefgh". Cas tres particulier: je n'ai
pas besoin de preciser la taille du tableau, le compilo se debrouille, et
va me faire un tableau de pile-poil 9 octets.
1 2 3 4 5