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

Avatar
Pascal J. Bourguignon
Kevin Denis writes:

Le 05-05-2011, Pascal J. Bourguignon a écrit :

Oui, mais ça n'a pas changé l'adresse de str. Lis mes programmes!



L'adresse de str n'a pas changée, mais la valeur stockée, elle, à changée
et pointe vers autre chose si j'ai bien compris?



Oui.

--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
Avatar
Antoine Leca
Alexandre Bacquart écrivit :
On 05/05/2011 04:19 PM, Marc Espie wrote:
In article<4dc2849b$0$27967$,
Alexandre Bacquart wrote:
Dans :
char *str = "abcdefgh";

Il y a deux choses : la chaîne "abcdefgh" (que tu ne peux pas modifier)
et le pointeur str (initialisé sur cette chaîne). Alors que dans :

char tab[] = "abcdefgh";

Il n'y a que la chaîne (toujours pas modifiable) *identifiée* par s.
Aucun pointeur. Ici, tab n'est pas un pointeur, mais identifie la
chaîne.






<...>
Dans ce code, au moment de l'execution, il n'y a pas de chaine
"abcdefgh".



Il faut bien qu'elle soit quelque-part.



Non (et cf. infra).

Ce que veut dire Marc (ÀMHA), c'est que tu devrais écrire le _tableau_
abcdefgh (ou {...}, c'est pareil) ci-dessus.

Ce qui importe, c'est que passée la phase de lecture syntaxique qui
enlève le goût sucré, il ne reste plus qu'un tableau d'élément d'un type
élémentaire, en l'occurrence char.
Et au moment de l'exécution, il n'y probablement même plus de tableau,
d'ailleurs (la norme C n'oblige pas à garder cette information, c'est la
grande différence avec d'autres langages).

mais en général, la chaîne est bel et bien dans le seg[...]



Euh là, tu t'enferres...


le sucre syntaxique, c'est super, mais ça contribue à cacher des
mécanismes qui autrement apparaîtraient plus nettement aux débutants.



On est bien d'accord.


Quand tu utilises tab ailleurs dans le programme, il se passe
quelque-chose d'assez subtil : un pointeur temporaire est créé pour que
tab se comporte comme un pointeur, comme dans :



Non. Rien n'est cree. Un tableau, dans un contexte qui a besoin d'un
pointeur, est considere exactement comme un pointeur, constant de sucroit.





+1
Et cela se passe au moment où le compilateur transforme le code en une
représentation interne (normalement un arbre syntaxique typé), qui est
ensuite fortement secoué par les opérations d'optimisation avant d'être
à nouveau transformé en code généré, bien plus tard.

Dans le code généré, la chaîne est accédé à travers un registre
d'adresse,



Pas forcément. Si le tableau possède 3 caractères plus le 0 final), il y
a de bonnes chances que tu trouves des compilateurs pour le manipuler
directement comme un bloc de 4 bytes pour certaines opérations, par
exemple en utilisant des registres de données (qui comme tu le sais
sûrement, n'ont pas d'adresses que l'on puisse ranger dans un registre
d'addresse.) Ou bien, le compilateur va calculer tout seul l'effet du
code C, et supprimer toute référence au tableau, qui n'existera tout
simplement plus du tout (et donc n'aura pas d'adresse non plus). Dans
d'autres cas (manque d'optimisation, ou au contraire optimisation de
localité ou d'alignement), le compilateur peut générer de multiples
copies dans le code généré de ce qui apparaît comme une seule chaîne au
départ... Remarque que dans ton exemple ci-dessus, si le besoin s'en
fait sentir, c'est l'objet dénoté par tab, pas la chaîne, qui aura
toujours la même adresse pour un contexte d'exécution donné.

Et le fait que ça ait été une chaîne au départ ne change absolument rien
au processus, tout autre tableau initialisé se comporterait de la même
manière.



Antoine
Avatar
espie
In article <4dc2b7dd$0$25128$,
Alexandre Bacquart wrote:
Ta vision des choses est legerement differente de la realite.



Je ne crois pas.

Dans ce code, au moment de l'execution, il n'y a pas de chaine "abcdefgh".



Il faut bien qu'elle soit quelque-part. Insinuerais-tu que c'est une
chaîne procédurale ? :) Je ne sais pas d'ailleurs si la norme autorise
implicitement ce genre de pratique, mais en général, la chaîne est bel
et bien dans le segment de données à l'exécution. Dire qu'il n'y a pas
de chaîne me semble confus, au contraire.



C'est un probleme de vocabulaire. Une chaine de caracteres, et un tableau
de caracteres, du point de vue du langage, ca n'est pas la meme chose.

Or, on est dans de l'interpretation precise du langage, donc autant employer
des termes techniques precis, ce que tu ne fais pas.

La chaine n'a qu'une existence pendant la compilation, c'est un initialiseur
pour un tableau. Le code ecrit est strictement equivalent a:
char tab[9] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 0 };
ce que tu as la, c'est juste du sucre syntaxique:



Oui. Quitte à chipoter (et être plus clair), j'aurais plutôt écris ''
à la fin, mais oui... le sucre syntaxique, c'est super, mais ça
contribue à cacher des mécanismes qui autrement apparaîtraient plus
nettement aux débutants.



les debutants qui font du C ont de toutes facons interet a serieusement
s'accrocher !

Dans ce contexte precis, je prefere mettre un 0. De toutes facons, c'est
un char, donc une valeur entiere. Le '' aurait plus tendance a donner
l'impression au debutant que c'est autre chose, alors que c'est un des points
fourbes du C: il n'y a pas de typage tres precis sur les valeurs numeriques
(par contre, on fait des arguties sur des points de detail, comme le fait
qu'une chaine de caracteres telle que "abcdefgh" est une constante.

Dans le code généré, la chaîne est accédé à travers un registre
d'adresse, l'équivalent en assembleur d'un pointeur. Quand je dis
"temporaire", je ne suppose pas qu'un emplacement mémoire temporaire est
créé à son intention (encore que le compilateur fait bien ce qu'il
veut). Mais cela me semble plus simple de l'expliquer comme cela.



Bof, bof, bof. Si tu as une variable char *s; quelque part, par exemple
globale, cette variable sera stockee quelque part en memoire. Si tu
la manipules un tant soit peu, le compilateur va charger sa valeur dans
un registre, et travailler dessus. Tu vas me dire aussi que ca fait
un pointeur temporaire ? il n'y a aucune difference par rapport au cas
du tableau precedemment cite..! donc au final, qu'est-ce qu'il y a de
si particulier concernant les tableaux ?

Au niveau manipulation pendant l'execution du programme, pas grand chose !

Un tableau comme un pointeur vont te permettre d'acceder a un espace memoire,
dans les deux cas, en partant d'une adresse memoire (typee).

La seule difference, c'est l'espace de stockage associe.
Avatar
Alexandre Bacquart
On 05/05/2011 07:15 PM, Antoine Leca wrote:
Alexandre Bacquart écrivit :
On 05/05/2011 04:19 PM, Marc Espie wrote:
Dans ce code, au moment de l'execution, il n'y a pas de chaine
"abcdefgh".



Il faut bien qu'elle soit quelque-part.



Non (et cf. infra).

Ce que veut dire Marc (ÀMHA), c'est que tu devrais écrire le _tableau_
abcdefgh (ou {...}, c'est pareil) ci-dessus.

Ce qui importe, c'est que passée la phase de lecture syntaxique qui
enlève le goût sucré, il ne reste plus qu'un tableau d'élément d'un type
élémentaire, en l'occurrence char.
Et au moment de l'exécution, il n'y probablement même plus de tableau,
d'ailleurs (la norme C n'oblige pas à garder cette information, c'est la
grande différence avec d'autres langages).



C'est assez clair, merci.

Dans le code généré, la chaîne est accédé à travers un registre
d'adresse,



Pas forcément. Si le tableau possède 3 caractères plus le 0 final), il y
a de bonnes chances que tu trouves des compilateurs pour le manipuler
directement comme un bloc de 4 bytes pour certaines opérations, par
exemple en utilisant des registres de données (qui comme tu le sais
sûrement, n'ont pas d'adresses que l'on puisse ranger dans un registre
d'addresse.) Ou bien, le compilateur va calculer tout seul l'effet du
code C, et supprimer toute référence au tableau, qui n'existera tout
simplement plus du tout (et donc n'aura pas d'adresse non plus)...



Certes, tout ceci est fort joli (et réel)... il y a tellement de façons
d'optimiser. Mais ce qui m'intéresse plus particulièrement, c'est la
valeur pédagogique de mon propos initial. En quoi mon explication
tendrait à mettre le doute dans la tête l'OP, voire à lui inculquer une
fausse conception (pour ce qu'il a besoin de savoir pour l'instant) ?

Je conçois bien que cette question n'est pas vraiment le sujet de forum,
mais la pédagogie, c'est important. Je suis curieux de savoir ce qui
cloche dans mon propos. Un problème de terminologie contextuelle
peut-être ? Ne peut-on pas, au moment de l'exécution, qualifier de
chaîne un tableau de char terminé par '' et initialement (à la
compilation) défini comme une chaîne ?

Autre chose : généralement, je préfère éviter le terme de tableau quand
je m'adresse à des débutants en C. Je sais bien que ce terme est adéquat
dans le cadre de la norme C, mais bien souvent, les débutants en C ont
une conception des tableaux autre que celle de la norme C et ils s'y
cassent les dents assez vite. D'où mon hésitation à utiliser ce terme,
en tous cas, pas sans développer le propos. Ca complique pas mal la
nécessité d'être concis pour ne pas l'embrouiller.



--
Alex
Avatar
Alexandre Bacquart
On 05/05/2011 07:20 PM, Marc Espie wrote:
In article<4dc2b7dd$0$25128$,
Alexandre Bacquart wrote:
Dans le code généré, la chaîne est accédé à travers un registre
d'adresse, l'équivalent en assembleur d'un pointeur. Quand je dis
"temporaire", je ne suppose pas qu'un emplacement mémoire temporaire est
créé à son intention (encore que le compilateur fait bien ce qu'il
veut). Mais cela me semble plus simple de l'expliquer comme cela.



Bof, bof, bof. Si tu as une variable char *s; quelque part, par exemple
globale, cette variable sera stockee quelque part en memoire. Si tu
la manipules un tant soit peu, le compilateur va charger sa valeur dans
un registre, et travailler dessus. Tu vas me dire aussi que ca fait
un pointeur temporaire ? il n'y a aucune difference par rapport au cas
du tableau precedemment cite..! donc au final, qu'est-ce qu'il y a de
si particulier concernant les tableaux ?



Mon intention était plutôt de faire comprendre à l'OP que la différence
entre les 2 expressions était le nombre d'objets créés (2 dans le cas
char*, 1 dans le cas char[]). Le reste, les détails d'implémentation,
j'ai pris la liberté de croire qu'il s'en tamponnait le coquillard :)



--
Alex
Avatar
espie
In article <4dc2eda8$0$7882$,
Alexandre Bacquart wrote:
On 05/05/2011 07:20 PM, Marc Espie wrote:
Bof, bof, bof. Si tu as une variable char *s; quelque part, par exemple
globale, cette variable sera stockee quelque part en memoire. Si tu
la manipules un tant soit peu, le compilateur va charger sa valeur dans
un registre, et travailler dessus. Tu vas me dire aussi que ca fait
un pointeur temporaire ? il n'y a aucune difference par rapport au cas
du tableau precedemment cite..! donc au final, qu'est-ce qu'il y a de
si particulier concernant les tableaux ?



Mon intention était plutôt de faire comprendre à l'OP que la différence
entre les 2 expressions était le nombre d'objets créés (2 dans le cas
char*, 1 dans le cas char[]). Le reste, les détails d'implémentation,
j'ai pris la liberté de croire qu'il s'en tamponnait le coquillard :)



D'une part, c'est faux. Ce que tu decris est, au plus, un detail
d'implementation. C'est quoi un objets cree ?

D'autre part, ce ne sont pas les points importants ici.
Ce qui importe c'est de savoir ce qu'on peut modifier legalement.
Dans char *s = "coucou"; la valeur pointee est constante.
Dans char t[] = "coucou"; c'est l'adresse qui pointe qui est constante.

Cote pedagogie, le C souffre mal l'approximation. Si tu te construis un
modele du langage qui est juste proche de la realite, tot ou tard tu vas
faire des choses fausses...
Avatar
Kevin Denis
Le 05-05-2011, Marc Espie a écrit :
D'autre part, ce ne sont pas les points importants ici.
Ce qui importe c'est de savoir ce qu'on peut modifier legalement.
Dans char *s = "coucou"; la valeur pointee est constante.
Dans char t[] = "coucou"; c'est l'adresse qui pointe qui est constante.



Mais dans char t[] = "coucou"; l'adresse qui pointe est constante, c'est
à dire &t donc. Je ne peux pas modifier la valeur de &t. Mais est-ce que
je peux modifier "coucou"?
--
Kevin
Avatar
Antoine Leca
Alexandre Bacquart écrivit :
Je suis curieux de savoir ce qui cloche dans mon propos.



Dans le propos initial, ou dans la réponse que tu fis à la réponse de
Marc (et qui est le message auquel j'ai répondu) ?


Un problème de terminologie contextuelle peut-être ?



Intéressante discussion, et qui aurait pu justifier de l'ouverture d'une
enfilade séparée : vaut-il mieux réutiliser le terme chaîne en dehors de
son contexte (ta position, qui est proche de la manière souvent utiliser
pour enseigner la physique) ; ou bien expliquer la réalité du
fonctionnement même si cela nécessite redéfinir des termes que certains
peuvent estimer acquis (la position de Marc, qui est la manière avec
laquelle on enseigne les mathématiques dites modernes) ?


Autre chose : généralement, je préfère éviter le terme de tableau quand
je m'adresse à des débutants en C. [...] Ca complique pas mal la
nécessité d'être concis pour ne pas l'embrouiller.



Pas sûr de comprendre si le facteur de complexité est le fait de parler
des tableaux, ou de l'éviter.

De toutes manières, après le premier problème que sont les pointeurs,
tout débutant en C doit comprendre les notions C de tableaux, de
l-values et de stockage des objets (qui sont liées entre elles) s'il
veut pouvoir progresser.


Antoine
Avatar
Marc Boyer
Le 05-05-2011, Alexandre Bacquart a écrit :
Certes, tout ceci est fort joli (et réel)... il y a tellement de façons
d'optimiser. Mais ce qui m'intéresse plus particulièrement, c'est la
valeur pédagogique de mon propos initial. En quoi mon explication
tendrait à mettre le doute dans la tête l'OP, voire à lui inculquer une
fausse conception (pour ce qu'il a besoin de savoir pour l'instant) ?



Ayant enseigné le C un certain nombre d'années, j'aurais tendance à
dire que ton propos entrainait une confusion, car il supposait que
dans l'écriture
char str[]= "abdcef";
il y a *deux* objets (str, et "abcdef"), alors qu'il n'y en a qu'un.
Alors que dans
char* ptr = "abcdef";
il y a bien deux objets, prt et "abcdef".

Je conçois bien que cette question n'est pas vraiment le sujet de forum,
mais la pédagogie, c'est important. Je suis curieux de savoir ce qui
cloche dans mon propos. Un problème de terminologie contextuelle
peut-être ? Ne peut-on pas, au moment de l'exécution, qualifier de
chaîne un tableau de char terminé par '' et initialement (à la
compilation) défini comme une chaîne ?



Tout tableau de char terminé par un '' est une chaine.

Autre chose : généralement, je préfère éviter le terme de tableau quand
je m'adresse à des débutants en C. Je sais bien que ce terme est adéquat
dans le cadre de la norme C, mais bien souvent, les débutants en C ont
une conception des tableaux autre que celle de la norme C et ils s'y
cassent les dents assez vite. D'où mon hésitation à utiliser ce terme,
en tous cas, pas sans développer le propos. Ca complique pas mal la
nécessité d'être concis pour ne pas l'embrouiller.



Comment evites-tu de parler de 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
Antoine Leca
Kevin Denis écrivit :
Mais dans char t[] = "coucou"; l'adresse qui pointe est constante, c'est
à dire &t donc. Je ne peux pas modifier la valeur de &t. Mais est-ce que
je peux modifier "coucou"?



C'est toute la discussion entre Marc et Alexandre. Pour résumer,
"coucou" est une constante, exactement comme 3.14159265. Tu ne peux pas
décider au cours du programme que ce sera plutôt 3.14150265,
n'est-ce-pas? Donc dans la même idée, "coucou" est, "coucou" restera.

Dans un deuxième temps, tu as les objets. Dans float e=3.14159265,
3.14159265 est la valeur initiale de l'objet, donc non modifiable sans
recompiler le programme. Par contre, au cours du programme il est
possible de modifier la valeur de l'objet e.
Exactement de la même manière, dans char t[] = "coucou", au delà de
l'initialisation, la «valeur» peut être modifiée par la suite ; mais
comme c'est un tableau, donc une donnée complexe, il faut intervenir sur
les éléments soit un par un (t[3]='s'), soit en groupe en utilisant des
fonctions comme memcpy().


Antoine