OVH Cloud OVH Cloud

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 Tue, 3 May 2011 18:18:34 +0000 (UTC),
Marc Espie écrivait :
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.



Je vais poser une question sans doute idiote, mais lorsqu'on essaye
d'accéder en écriture à une zone mémoire RO, ça ne devrait pas
terminer par une violation d'accès ?

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



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
Jean-Marc Bourguet
JKB writes:

Je vais poser une question sans doute idiote, mais lorsqu'on essaye
d'accéder en écriture à une zone mémoire RO, ça ne devrait pas
terminer par une violation d'accès ?



C'est le comportement que j'ai ici sans optimisation. Avec optimisation
j'ai le même que Marc. Quand les optimiseurs savent qu'ils peuvent faire
n'importe quoi avec des comportements indéfinis, ils font n'importe quoi...

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
Samuel DEVULDER
Le 03/05/2011 15:25, Kevin Denis a écrit :

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 + )?



Quand tu suppose qu'implicitement malloc() a été appelé pour la chaine
"abcdefgh" tu fais une erreur. Il n'y a rien d'implicite en C
(l'implicite c'est bon pour les langages de plus haut niveau). En C il
faut tout faire soit-même à la main: malloc() free() par exemple). Si
malloc() il y a, alors tu dois trouver un malloc dans le code ou la
bibliothèque (en simplifiant). Or il n'y a rien de tout cela.

Malloc/calloc/realloc/free s'occupent d'allouer de la mémoire dans le
tas, or la chaine se trouve dans la zone de mémoire statique du
programme. Si on tente de faire un free() sur une zone statique on a
droit à un coredump. C'est ce que tu obtiens avec le realloc(). [en
fait realloc() peut utiliser un free() en interne, d'où coredump].

Pour bien comprendre ce qu'il se passe, il peut être utile de
schématiser l'organisation mémoire d'un programme et voir là ou le
compilo place les choses. Soit par exemple le code suivant

short a = 0x1122;
char tab[] = "ab";
char * str = "fg";

int f(void) {
short b = a;
char *ptr = str;
a = -b;
}

Alors on peut obtenir l'organisation mémoire suivante sur une
architecture big-endian 16bits:
============================================================ $0000 <-- Début mémoire. Zone reservée système.
:
----- Zone du code.
:
$0012 C0 <-- début de l'implementation de f()
$0013 DE
:
----- Zone des données globales (initialisées ou pas).
:
$0034 11 <-- adresse de "a"
$0035 22
:
$3344 'a' <-- adresse de "tab"
$3345 'b'
$3346 ''
:
$5566 'f' <-- adresse de "str[0]"
$5567 'g'
$5568 ''
:
$7788 55 <-- adresse de str
$7789 66
:
----- Zone du tas. (malloc/free utilisent cette zone)
: / / /
TAS / / /
: / / /
~~~~~ <-- Sommet du tas (mobile)
/////
///// Zone non utilisée.
/////
~~~~~ <-- Sommet de la pile (mobile)
: / / /
PILE / / /
: / / /
$EEEE 55 <-- adresse de "ptr"
$EEEF 66
$EEF0 11 <-- adresse de "b"
$EFF1 22
----- Zone système.
:
$FFFF Fin mémoire.
============================================================
Comme tu peux le voir, "str" existe et se trouve à l'adresse mémoire
$7788-$7789 et il contient la valeur $5566, c'est à dire l'adresse de
"str[0]". Ce qui signifie que str[] a effectivement été alloué, mais
pas dans le tas, mais plutot dans la zone statique (typiquement
initialisée par le loader de l'OS.) Dans cette zone on y retrouve les
autres données globales.

A noter: la fin du tas est mobile et peut croitre vers les adresses
haute et le sommet de la pile peut décroire vers les adresses faible.
C'est pour cela qu'on parle de zone mémoire dynamique à l'opposé de la
zone de mémoire statique.

Je sais pas si mon "ascii-art" est clair, mais j'ai toujours trouvé
qu'avoir conscience de l'organisation mémoire permet de comprendre
que rien n'est magique en C. Tout marche bien parce que c'est
"organisé pour" et que tout le monde utilise correctement la règle. Si
l'on a des accès mémoire illégaux, ca n'est pas un bug dans un hasard,
il y a forcément une vraie raison (par exemple le fait qu'on continue à
accéder à "b" alors qu'il est à présent hors pile, dans la zone non
utilisée (le sommet de la pile est mobile).

sam.
Avatar
JKB
Le Tue, 3 May 2011 22:27:32 +0200,
Jean-marc écrivait :
Jean-Marc Bourguet wrote:
JKB writes:

Je vais poser une question sans doute idiote, mais lorsqu'on essaye
d'accéder en écriture à une zone mémoire RO, ça ne devrait pas
terminer par une violation d'accès ?



C'est le comportement que j'ai ici sans optimisation. Avec
optimisation j'ai le même que Marc. Quand les optimiseurs savent
qu'ils peuvent faire n'importe quoi avec des comportements indéfinis,
ils font n'importe quoi...



Et c'est toute la beauté des "comportements indéfinis".
Avec le compilateur C de Visual Studio 6, on a en mode debug
une violation mémoire alors qu'en release, il affiche
"abedefgh", quel que soit le niveau d'optimisation demandé.
Encore un comportement différent...



Merci pour la réponse...

Cordialement,

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
Kevin Denis
Le 03-05-2011, Marc Espie a écrit :
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.



Ok, et donc je peux modifier l'adresse de str, mais pas modifier les
données pointées par str. Donc si je veux modifier la chaine de caractères,
je dois écrire des caractères ailleurs, puis faire pointer str vers
le premier caractère de la chaine?

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



Ca se paramètre, non? Le programme en RO, c'est lié à openBSD?

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.



Mais si dans ce 2e exemple, je fais:
printf("%sn", str);
Le printf s'arrête car il rencontre un . Mais alors si me prends
l'idée de faire:
str[8] = 'e';
alors le printf va lire abcdefghe et ensuite? Tous les octets jusqu'a
rencontrer un ou sortir de la zone allouée (et donc une violation
de mémoire, i.e. segfault)?

Merci
--
Kevin
Avatar
Kevin Denis
Le 03-05-2011, Samuel DEVULDER a écrit :
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 + )?



Quand tu suppose qu'implicitement malloc() a été appelé pour la chaine
"abcdefgh" tu fais une erreur. Il n'y a rien d'implicite en C
(l'implicite c'est bon pour les langages de plus haut niveau). En C il
faut tout faire soit-même à la main: malloc() free() par exemple). Si
malloc() il y a, alors tu dois trouver un malloc dans le code ou la
bibliothèque (en simplifiant). Or il n'y a rien de tout cela.



Ok. Le malloc n'est pas appelé car la chaine est dans le code du programme..

(snip..)
Je sais pas si mon "ascii-art" est clair,



Si si. J'enregistre ce message pour conservation.
Merci beaucoup.
--
Kevin
Avatar
Kevin Denis
Le 03-05-2011, Erwan David a écrit :

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



quel est le type de sizeof? Si j'utilise le retour de sizeof dans une
boucle for, donc avec un int, alors le compilateur me prévient que le
sizeof n'est pas un int. Je n'ai pas de man page pour sizeof (?)
$ man sizeof
Il n'y a pas de page de manuel pour sizeof.
--
Kevin
Avatar
Erwan David
Kevin Denis écrivait :

Le 03-05-2011, Erwan David a écrit :

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



quel est le type de sizeof? Si j'utilise le retour de sizeof dans une
boucle for, donc avec un int, alors le compilateur me prévient que le
sizeof n'est pas un int. Je n'ai pas de man page pour sizeof (?)
$ man sizeof
Il n'y a pas de page de manuel pour sizeof.



sizeof n'est pas une fonction, c'est une directive de compilation.
Son résultat est un size_t qui est un type entier non signé.


--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé
Avatar
Alexandre Bacquart
On 05/05/2011 11:08 AM, Kevin Denis wrote:
Le 03-05-2011, Erwan David a écrit :

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



quel est le type de sizeof?



Le type ? sizeof n'a pas de type, ce n'est pas une variable.

sizeof est un opérateur, au même titre que + ou *.

Si j'utilise le retour de sizeof dans une
boucle for, donc avec un int, alors le compilateur me prévient que le
sizeof n'est pas un int. Je n'ai pas de man page pour sizeof (?)
$ man sizeof
Il n'y a pas de page de manuel pour sizeof.



Ce n'est pas étonnant. sizeof ne fait pas partie de la bibliothèque C.
C'est un opérateur et un mot clé du langage.

C'est comme si tu faisais :

$ man +

ou

$ man while


--
Alex
Avatar
espie
In article ,
Kevin Denis wrote:
Le 03-05-2011, Marc Espie a écrit :
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.



Ok, et donc je peux modifier l'adresse de str, mais pas modifier les
données pointées par str. Donc si je veux modifier la chaine de caractères,
je dois écrire des caractères ailleurs, puis faire pointer str vers
le premier caractère de la chaine?

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



Ca se paramètre, non? Le programme en RO, c'est lié à openBSD?



Non, c'est un artefact de l'implementation. La norme dit explicitement
"undefined behavior". Ce qui veut dire qu'il peut se passer n'importe
quoi et que tu n'as pas ton mot a dire.

-> il y a des programmes C FAUX qui s'executent, ceci en est un exemple.
1 2 3 4 5