Ce que je fais là est un peu cavalier mais j'ouvre un fil pour répondre
au message de François, l'autre fil devenant vraiment trop anarchique.
Francois a écrit :
>
> « Le nom t d'un tableau est une *constante* de type pointeur dont la
> valeur est fixée une bonne fois pour toute au début de l'exécution du
> programme et n'est pas stockée en mémoire. »
>
> est-ce correct ? J'ai l'impression que oui bien sûr, mais je me trompe
> peut-être (sûrement).
Je ne comprends pas ton obstination à vouloir trouver une définition
parfaite de "nom d'un tableau", ce qui compte aussi c'est de savoir les
utiliser ces tableaux mais peut-être que tu ne pourras faire cela
qu'après avoir défini, ce en quoi on reconnait le matheux ;) .
Et maintenant tu compliques les choses en parlant non plus de tableau
mais de nom d'un tableau.
À mon avis, ta définition est totalement incorrecte même si je
vois bien l'idée.
"Un nom de variable est une constante" ? Bof, dans la
norme (que je la connais très mal, OK), les constantes et les noms de
variables (les identificateurs) ne sont pas dans la même catégorie de
"token" donc on ne peut pas dire formellement qu'un identificateur est
une constante.
"Un tableau est de type pointeur", c'est une horreur, un tableau est de
type tableau, c'est tout même si une expression de type tableau est,
dans certaines circonstances, convertie en pointeur.
La valeur de t est fixée une bonne fois pour toutes au début ? oui, me
semble-t-il et encore, je sens que certains vont chipoter.
"La valeur n'est pas stockée en mémoire" : à mon avis, non, un tableau
est un objet, comment on y accèderait sinon ?
Comme dit la faq de clc à propos de l'expression "un tableau est un
pointeur constant" (question 6.9) : un nom de tableau est constant dans
la mesure où on ne peut l'assigner mais une fois de plus, un tableau
n'est pas un pointeur.
Ben oui, le compilateur la calcule, mais ensuite, dans le code généré, il n'y a aucun besoin de la conserver.
Par exemple: double foo(void){ double p[10]; double q[10]; double r[10];
p[6]= 1.0; q[3]= 3.0; r[4]= 4.0; return p[6]+q[3]+r[4]; } Aucun besoin pour le compilo de laisser dans l'exécutable les adresses de début de p, q et r.
D'accord, je n'avais pas pensé à ce genre d'exemples, après tout c'est vrai, il suffit que le compilateur écrive le code qui permette l'exécution de la fonction foo() par quel que moyen que ce soit.
Non, tel n'est pas le sens de mon propos. Sinon, autant écrire return 8.0; Non, ce que je dis, c'est que le compilo génère des instructions d'accès, sans ce soucier de savoir si on est au début, à la fin, etc. Je peux refaire l'exemple en un rien plus complexe qui force le compilo à garder p, q et r, sans que le code de foo ne connaisse ni leurs tailles, ni leurs adresse de début.
Dans le même genre void bar(double d){ double x; double t[5]; x= 0; for(int i=0;i<5;++i){ t[i]= i+1; d+= t[i]; } } et void bar(double d){ double t[6]; t[0]= 0; for(int i=1;i<6;++i){ t[i]= i; t[0]+= t[i]; } } risquent de donner lieu à exactement le même code, car les accès mémoires sont surement les même, et il n'y a aucun besoin de "stoquer" le début de t.
OK, je crois que je n'avais pas perçu le degré de liberté que possède le compilateur.
Là, il ne s'agit pas de "dégré de liberté" du compilo. Il s'agit simplement du fait que la case mémoire qui sert d'accumulateur s'appelle une fois 'd' et l'autre 't[0]', dans le code source. Dans le code généré, elle sera surement devenue StackPointeur+4 (par exemple) et les cases parcourues StackPointeur+8 à StackPointeur+14, dans les deux codes.
Il me semble même que le compilateur peut ne pas générer le code d'une fonction qui ne fait rien.
En effet. J'ai hésité à ajouter des printf pour forcer le compilo à considérer des effets de bord.
Marc Boyer
On 2008-03-15, candide <c.candide@free.fr> wrote:
Ben oui, le compilateur la calcule, mais ensuite, dans
le code généré, il n'y a aucun besoin de la conserver.
Par exemple:
double foo(void){
double p[10];
double q[10];
double r[10];
p[6]= 1.0;
q[3]= 3.0;
r[4]= 4.0;
return p[6]+q[3]+r[4];
}
Aucun besoin pour le compilo de laisser dans l'exécutable
les adresses de début de p, q et r.
D'accord, je n'avais pas pensé à ce genre d'exemples, après tout c'est
vrai, il suffit que le compilateur écrive le code qui permette
l'exécution de la fonction foo() par quel que moyen que ce soit.
Non, tel n'est pas le sens de mon propos. Sinon, autant écrire
return 8.0;
Non, ce que je dis, c'est que le compilo génère des instructions
d'accès, sans ce soucier de savoir si on est au début, à la fin, etc.
Je peux refaire l'exemple en un rien plus complexe qui force
le compilo à garder p, q et r, sans que le code de foo ne connaisse
ni leurs tailles, ni leurs adresse de début.
Dans le même genre
void bar(double d){
double x;
double t[5];
x= 0;
for(int i=0;i<5;++i){
t[i]= i+1;
d+= t[i];
}
}
et
void bar(double d){
double t[6];
t[0]= 0;
for(int i=1;i<6;++i){
t[i]= i;
t[0]+= t[i];
}
}
risquent de donner lieu à exactement le même code, car
les accès mémoires sont surement les même, et il n'y a
aucun besoin de "stoquer" le début de t.
OK, je crois que je n'avais pas perçu le degré de liberté que possède le
compilateur.
Là, il ne s'agit pas de "dégré de liberté" du compilo. Il s'agit
simplement du fait que la case mémoire qui sert d'accumulateur s'appelle
une fois 'd' et l'autre 't[0]', dans le code source. Dans le code
généré, elle sera surement devenue StackPointeur+4 (par exemple) et
les cases parcourues StackPointeur+8 à StackPointeur+14, dans les
deux codes.
Il me semble même que le compilateur peut ne pas générer le code d'une
fonction qui ne fait rien.
En effet. J'ai hésité à ajouter des printf pour forcer le compilo à
considérer des effets de bord.
Ben oui, le compilateur la calcule, mais ensuite, dans le code généré, il n'y a aucun besoin de la conserver.
Par exemple: double foo(void){ double p[10]; double q[10]; double r[10];
p[6]= 1.0; q[3]= 3.0; r[4]= 4.0; return p[6]+q[3]+r[4]; } Aucun besoin pour le compilo de laisser dans l'exécutable les adresses de début de p, q et r.
D'accord, je n'avais pas pensé à ce genre d'exemples, après tout c'est vrai, il suffit que le compilateur écrive le code qui permette l'exécution de la fonction foo() par quel que moyen que ce soit.
Non, tel n'est pas le sens de mon propos. Sinon, autant écrire return 8.0; Non, ce que je dis, c'est que le compilo génère des instructions d'accès, sans ce soucier de savoir si on est au début, à la fin, etc. Je peux refaire l'exemple en un rien plus complexe qui force le compilo à garder p, q et r, sans que le code de foo ne connaisse ni leurs tailles, ni leurs adresse de début.
Dans le même genre void bar(double d){ double x; double t[5]; x= 0; for(int i=0;i<5;++i){ t[i]= i+1; d+= t[i]; } } et void bar(double d){ double t[6]; t[0]= 0; for(int i=1;i<6;++i){ t[i]= i; t[0]+= t[i]; } } risquent de donner lieu à exactement le même code, car les accès mémoires sont surement les même, et il n'y a aucun besoin de "stoquer" le début de t.
OK, je crois que je n'avais pas perçu le degré de liberté que possède le compilateur.
Là, il ne s'agit pas de "dégré de liberté" du compilo. Il s'agit simplement du fait que la case mémoire qui sert d'accumulateur s'appelle une fois 'd' et l'autre 't[0]', dans le code source. Dans le code généré, elle sera surement devenue StackPointeur+4 (par exemple) et les cases parcourues StackPointeur+8 à StackPointeur+14, dans les deux codes.
Il me semble même que le compilateur peut ne pas générer le code d'une fonction qui ne fait rien.
En effet. J'ai hésité à ajouter des printf pour forcer le compilo à considérer des effets de bord.
Marc Boyer
Francois
On ne peut pas le faire avec un pointeur.
int *f();
f() = NULL;
n'est pas valide. Le pointeur résultant de la conversion implicite est une valeur, pas un objet.
Oui, on est d'accord. Donc en fait, en me basant sur ce que vous avez écrit plus bas, on pourrait affiner l'esprit de la règle en disant ceci :
-------------------------Règle------------------------- « Dans les contextes qui ne peuvent pas prendre un tableau, celui-ci est converti implicitement en une *valeur* de type pointeur vers son premier élément. » -------------------------------------------------------
Ainsi, on lève l'ambiguïté dont vous avez parlé. Au passage, essayons d'être précis jusqu'au bout, les contextes qui admettent un tableau (et où il n'y a pas conversion) sont :
1) sizeof(tab) 2) &tab 3) et enfin l'initialisation en une chaîne de caractères char tab[10]="Bonjour" ;
Il n'y en a pas d'autres ?
Allez, je tente quelque chose :
Avec "int tab[10] ;" - "variable de type tableau" ---> tab[2] - "expression de type tableau" ---> tab
Est-ce juste ?
Non. tab est le nom d'une variable de type tableau de 10 int. le nom d'une variable d'un type donné est aussi une expression du même type, donc tab est aussi une expression de type tableau de 10 int.
Je voudrais être sûr de bien comprendre.
1) Toute variable de type tableau est une expression de type tableau.
2) Mais la réciproque est fausse. Quand j'ai ceci "int tab2[10][12];", "tab2[7]" est une expression de type "tableau de 10 int" mais pas une variable de type tableau. Par contre "tab2" est une variable de type tableau. L'occurrence, il faudrait dire comment exactement ? "tab2" est une variable de type tableau de *10x12 int* ?
tab[2] est une expression de type int. la manière dont cette expression est analysée est la suivante: tab est une expression de type tableau de 10 int, elle n'est pas dans un contexte ou les expressions de type tableau sont valides, donc il y a une conversion implicite vers un pointeur vers int. Ce pointeur est le premier argument de [], le deuxième argument étant 2.
Aye ! Là je crains ne pas comprendre. Et pourtant, je crois que c'est justement ce point qui me paraît crucial.
1) Vous dites : « elle n'est pas dans un contexte ou les expressions de type tableau sont valides, donc il y a une conversion implicite vers un pointeur vers int». Là, c'est OK pour moi. Je dirais même « conversion en une *valeur* de type "pointeur sur int ».
2) Vous dites : « Ce pointeur est le premier argument de [], le deuxième argument étant 2 ». Là, en revanche, je ne comprends pas du tout. Ce pointeur est « le premier argument de [] » ? Serait-ce possible d'avoir plus de détails ? Une conversion implicite de "tab" en une valeur de type "pointeur sur int" doit bien avoir lieu d'après "notre règle" (celle énoncée au début de ce message). Mais ce qui me gêne, c'est qu'au final, tab[2] est une expression de type int, et donc il doit bien avoir une opération d'indirection (avec *) qui apparaît. Or dans ce que vous avez écrit, j'ai l'impression que vous ne parlez pas de l'opérateur *.
Je vais vous dire comment je vois les choses. Peut-être que c'est finalement ce que vous voulez dire, on ne sait jamais ?
Quand on a "tab[2]" : 1) d'après notre règle, "tab" est converti en &tab[0], si bien qu'on a en quelque sorte "(&tab[0])[2]" (c'est pas jojo, mais on s'en fout) 2) Par *définition*, (&tab[0])[2] est identique à *(&tab[0]+2). Voilà l'opérateur * qui apparaît.
Plus j'y pense, plus j'ai l'impression que ce que j'ai dit dans mes *tout se passe comme si* revient dans les faits exactement à ce que vous dites. Sauf que ma vision est "alambiquée", j'ai l'impression que votre vision (que, hélas, je ne comprends pas encore complètement, mais j'y travaille) à un côté systématique ce qui est forcément une bonne chose. C'est pour ça que j'aimerais bien la comprendre.
Il y a trois choses qu'il ne faut pas confondre, mais malheureusement on a tendance à utiliser les mêmes mots. Ca ne pose en pratique peu de problèmes une fois qu'on a compris qu'il pouvait y avoir plusieurs choses.
Ces trois choses à ne pas confondre, si j'ai bien compris, c'est lorsqu'on parle de type (int par exemple), il faut distinguer si l'on parle :
1) du type d'une manière générale. 2) du type d'un objet (zone de la mémoire qui stocke des valeurs) 3) du type d'une valeur (qui peut parfois être le contenu d'un objet et dans ce cas, le type de l'objet et le type de sa valeur sont identiques)
C'est bien cela ?
Tant que j'y suis, ceci est-il correct ?
1) Objet = zone de la mémoire formée de bytes consécutifs, accessible en lecture et parfois en écriture.
2) Variable = objet, toujours accessible en lecture et écriture, qui porte un nom
Souvent, en C, on parle d'expression ("expression de type tableau" etc.). Y a-t-il une définition simple de ce qu'est une expression en C ?
Merci encore pour vos réponses qui me permettent vraiment d'y voir plus clair.
François
On ne peut pas le faire avec un pointeur.
int *f();
f() = NULL;
n'est pas valide. Le pointeur résultant de la conversion implicite est une
valeur, pas un objet.
Oui, on est d'accord. Donc en fait, en me basant sur ce que vous avez
écrit plus bas, on pourrait affiner l'esprit de la règle en disant ceci :
-------------------------Règle-------------------------
« Dans les contextes qui ne peuvent pas prendre un tableau, celui-ci est
converti implicitement en une *valeur* de type pointeur vers son premier
élément. »
-------------------------------------------------------
Ainsi, on lève l'ambiguïté dont vous avez parlé. Au passage, essayons
d'être précis jusqu'au bout, les contextes qui admettent un tableau (et
où il n'y a pas conversion) sont :
1) sizeof(tab)
2) &tab
3) et enfin l'initialisation en une chaîne de caractères
char tab[10]="Bonjour" ;
Il n'y en a pas d'autres ?
Allez, je tente quelque chose :
Avec "int tab[10] ;"
- "variable de type tableau" ---> tab[2]
- "expression de type tableau" ---> tab
Est-ce juste ?
Non.
tab est le nom d'une variable de type tableau de 10 int.
le nom d'une variable d'un type donné est aussi une expression du
même type, donc tab est aussi une expression de type tableau de 10 int.
Je voudrais être sûr de bien comprendre.
1) Toute variable de type tableau est une expression de type tableau.
2) Mais la réciproque est fausse. Quand j'ai ceci "int tab2[10][12];",
"tab2[7]" est une expression de type "tableau de 10 int" mais pas une
variable de type tableau. Par contre "tab2" est une variable de type
tableau.
L'occurrence, il faudrait dire comment exactement ? "tab2" est une
variable de type tableau de *10x12 int* ?
tab[2] est une expression de type int.
la manière dont cette expression est analysée est la suivante:
tab est une expression de type tableau de 10 int, elle n'est pas
dans un contexte ou les expressions de type tableau sont valides,
donc il y a une conversion implicite vers un pointeur vers int.
Ce pointeur est le premier argument de [], le deuxième argument
étant 2.
Aye ! Là je crains ne pas comprendre. Et pourtant, je crois que c'est
justement ce point qui me paraît crucial.
1) Vous dites : « elle n'est pas dans un contexte ou les expressions de
type tableau sont valides, donc il y a une conversion implicite vers un
pointeur vers int». Là, c'est OK pour moi. Je dirais même « conversion
en une *valeur* de type "pointeur sur int ».
2) Vous dites : « Ce pointeur est le premier argument de [], le deuxième
argument étant 2 ». Là, en revanche, je ne comprends pas du tout. Ce
pointeur est « le premier argument de [] » ? Serait-ce possible d'avoir
plus de détails ?
Une conversion implicite de "tab" en une valeur de type "pointeur sur
int" doit bien avoir lieu d'après "notre règle" (celle énoncée au début
de ce message). Mais ce qui me gêne, c'est qu'au final, tab[2] est une
expression de type int, et donc il doit bien avoir une opération
d'indirection (avec *) qui apparaît. Or dans ce que vous avez écrit,
j'ai l'impression que vous ne parlez pas de l'opérateur *.
Je vais vous dire comment je vois les choses. Peut-être que c'est
finalement ce que vous voulez dire, on ne sait jamais ?
Quand on a "tab[2]" :
1) d'après notre règle, "tab" est converti en &tab[0], si bien qu'on a
en quelque sorte "(&tab[0])[2]" (c'est pas jojo, mais on s'en fout)
2) Par *définition*, (&tab[0])[2] est identique à *(&tab[0]+2). Voilà
l'opérateur * qui apparaît.
Plus j'y pense, plus j'ai l'impression que ce que j'ai dit dans mes
*tout se passe comme si* revient dans les faits exactement à ce que vous
dites. Sauf que ma vision est "alambiquée", j'ai l'impression que votre
vision (que, hélas, je ne comprends pas encore complètement, mais j'y
travaille) à un côté systématique ce qui est forcément une bonne chose.
C'est pour ça que j'aimerais bien la comprendre.
Il y a trois choses qu'il ne faut pas confondre, mais malheureusement on a
tendance à utiliser les mêmes mots. Ca ne pose en pratique peu de
problèmes une fois qu'on a compris qu'il pouvait y avoir plusieurs choses.
Ces trois choses à ne pas confondre, si j'ai bien compris, c'est
lorsqu'on parle de type (int par exemple), il faut distinguer si l'on
parle :
1) du type d'une manière générale.
2) du type d'un objet (zone de la mémoire qui stocke des valeurs)
3) du type d'une valeur (qui peut parfois être le contenu d'un objet et
dans ce cas, le type de l'objet et le type de sa valeur sont identiques)
C'est bien cela ?
Tant que j'y suis, ceci est-il correct ?
1) Objet = zone de la mémoire formée de bytes consécutifs, accessible en
lecture et parfois en écriture.
2) Variable = objet, toujours accessible en lecture et écriture, qui
porte un nom
Souvent, en C, on parle d'expression ("expression de type tableau"
etc.). Y a-t-il une définition simple de ce qu'est une expression en C ?
Merci encore pour vos réponses qui me permettent vraiment d'y voir plus
clair.
n'est pas valide. Le pointeur résultant de la conversion implicite est une valeur, pas un objet.
Oui, on est d'accord. Donc en fait, en me basant sur ce que vous avez écrit plus bas, on pourrait affiner l'esprit de la règle en disant ceci :
-------------------------Règle------------------------- « Dans les contextes qui ne peuvent pas prendre un tableau, celui-ci est converti implicitement en une *valeur* de type pointeur vers son premier élément. » -------------------------------------------------------
Ainsi, on lève l'ambiguïté dont vous avez parlé. Au passage, essayons d'être précis jusqu'au bout, les contextes qui admettent un tableau (et où il n'y a pas conversion) sont :
1) sizeof(tab) 2) &tab 3) et enfin l'initialisation en une chaîne de caractères char tab[10]="Bonjour" ;
Il n'y en a pas d'autres ?
Allez, je tente quelque chose :
Avec "int tab[10] ;" - "variable de type tableau" ---> tab[2] - "expression de type tableau" ---> tab
Est-ce juste ?
Non. tab est le nom d'une variable de type tableau de 10 int. le nom d'une variable d'un type donné est aussi une expression du même type, donc tab est aussi une expression de type tableau de 10 int.
Je voudrais être sûr de bien comprendre.
1) Toute variable de type tableau est une expression de type tableau.
2) Mais la réciproque est fausse. Quand j'ai ceci "int tab2[10][12];", "tab2[7]" est une expression de type "tableau de 10 int" mais pas une variable de type tableau. Par contre "tab2" est une variable de type tableau. L'occurrence, il faudrait dire comment exactement ? "tab2" est une variable de type tableau de *10x12 int* ?
tab[2] est une expression de type int. la manière dont cette expression est analysée est la suivante: tab est une expression de type tableau de 10 int, elle n'est pas dans un contexte ou les expressions de type tableau sont valides, donc il y a une conversion implicite vers un pointeur vers int. Ce pointeur est le premier argument de [], le deuxième argument étant 2.
Aye ! Là je crains ne pas comprendre. Et pourtant, je crois que c'est justement ce point qui me paraît crucial.
1) Vous dites : « elle n'est pas dans un contexte ou les expressions de type tableau sont valides, donc il y a une conversion implicite vers un pointeur vers int». Là, c'est OK pour moi. Je dirais même « conversion en une *valeur* de type "pointeur sur int ».
2) Vous dites : « Ce pointeur est le premier argument de [], le deuxième argument étant 2 ». Là, en revanche, je ne comprends pas du tout. Ce pointeur est « le premier argument de [] » ? Serait-ce possible d'avoir plus de détails ? Une conversion implicite de "tab" en une valeur de type "pointeur sur int" doit bien avoir lieu d'après "notre règle" (celle énoncée au début de ce message). Mais ce qui me gêne, c'est qu'au final, tab[2] est une expression de type int, et donc il doit bien avoir une opération d'indirection (avec *) qui apparaît. Or dans ce que vous avez écrit, j'ai l'impression que vous ne parlez pas de l'opérateur *.
Je vais vous dire comment je vois les choses. Peut-être que c'est finalement ce que vous voulez dire, on ne sait jamais ?
Quand on a "tab[2]" : 1) d'après notre règle, "tab" est converti en &tab[0], si bien qu'on a en quelque sorte "(&tab[0])[2]" (c'est pas jojo, mais on s'en fout) 2) Par *définition*, (&tab[0])[2] est identique à *(&tab[0]+2). Voilà l'opérateur * qui apparaît.
Plus j'y pense, plus j'ai l'impression que ce que j'ai dit dans mes *tout se passe comme si* revient dans les faits exactement à ce que vous dites. Sauf que ma vision est "alambiquée", j'ai l'impression que votre vision (que, hélas, je ne comprends pas encore complètement, mais j'y travaille) à un côté systématique ce qui est forcément une bonne chose. C'est pour ça que j'aimerais bien la comprendre.
Il y a trois choses qu'il ne faut pas confondre, mais malheureusement on a tendance à utiliser les mêmes mots. Ca ne pose en pratique peu de problèmes une fois qu'on a compris qu'il pouvait y avoir plusieurs choses.
Ces trois choses à ne pas confondre, si j'ai bien compris, c'est lorsqu'on parle de type (int par exemple), il faut distinguer si l'on parle :
1) du type d'une manière générale. 2) du type d'un objet (zone de la mémoire qui stocke des valeurs) 3) du type d'une valeur (qui peut parfois être le contenu d'un objet et dans ce cas, le type de l'objet et le type de sa valeur sont identiques)
C'est bien cela ?
Tant que j'y suis, ceci est-il correct ?
1) Objet = zone de la mémoire formée de bytes consécutifs, accessible en lecture et parfois en écriture.
2) Variable = objet, toujours accessible en lecture et écriture, qui porte un nom
Souvent, en C, on parle d'expression ("expression de type tableau" etc.). Y a-t-il une définition simple de ce qu'est une expression en C ?
Merci encore pour vos réponses qui me permettent vraiment d'y voir plus clair.
François
Francois
2) Mais la réciproque est fausse. Quand j'ai ceci "int tab2[10][12];", "tab2[7]" est une expression de type "tableau de 10 int" ...
Zut ! Je rectifie : "tableau de 12 int".
François
2) Mais la réciproque est fausse. Quand j'ai ceci "int tab2[10][12];",
"tab2[7]" est une expression de type "tableau de 10 int" ...
2) Mais la réciproque est fausse. Quand j'ai ceci "int tab2[10][12];", "tab2[7]" est une expression de type "tableau de 10 int" ...
Zut ! Je rectifie : "tableau de 12 int".
François
Thierry B.
--{ candide a plopé ceci: }--
Non, si je fais du C, le compilateur pour moi est une boite noire, je ne m'intéresse pas à la façon dont le compilateur implémente telle ou telle fonctionnalité, chacun son boulot, pour moi le comportement visible du compilateur est défini par la norme et je n'ai pas à sortir de là,la
Qaund j'ai débuté ma carrière de programmeur du dimanche, je n'avais que le K&R1 et la doc du compilo (Metacomco C sur ST) pour apprendre. Je n'ai commancé qu'a bien appréhender les choses que quand je suis allé lire le code généré, et que j'ai essayé de refaire manuellement ce que faisais le compilateur. Bon, sur un 68k que l'on pratique déja bien, c'est plus facile qu'un i386 (et la syntaxe gnu :) mais c'est _très_ instructif.
Regarde le code machine qui resulte de char a[] = "bonjour"; char *b = "bonjour";
Une fois de plus, ça te dérangerait de t'exprimer clairement. La question était (autant qu'elle ait un sens comme l'a fait remarquer JMB) :
Je trouve que c'est assez clair, non ? Il suffit de compiler un bout de code, et de regarder ce que ça donne...
Il est possible que je sois passé à côté de quelque chose, je ne suis qu'un programmeur du dimanche et je manque en effet d'imagination, mais je ne vois pas comment on peut accéder à un objet s'il n'est pas stocké en mémoire.
Il est stocké en mémoire, certe, mais son adresse est, comment dire, fantômatique :)
-- L'avis de décès de regroupement du groupe avec un groupe père pourrait ainsi être publié post-mortem, évitant ainsi les votes désolants et surtout les résurrections artificielles. --{ fufe et le Vaudou }--
--{ candide a plopé ceci: }--
Non, si je fais du C, le compilateur pour moi est une boite noire, je ne
m'intéresse pas à la façon dont le compilateur implémente telle ou telle
fonctionnalité, chacun son boulot, pour moi le comportement visible du
compilateur est défini par la norme et je n'ai pas à sortir de là,la
Qaund j'ai débuté ma carrière de programmeur du dimanche, je
n'avais que le K&R1 et la doc du compilo (Metacomco C sur ST)
pour apprendre. Je n'ai commancé qu'a bien appréhender les choses
que quand je suis allé lire le code généré, et que j'ai essayé
de refaire manuellement ce que faisais le compilateur. Bon, sur
un 68k que l'on pratique déja bien, c'est plus facile qu'un i386
(et la syntaxe gnu :) mais c'est _très_ instructif.
Regarde le code machine qui resulte de
char a[] = "bonjour";
char *b = "bonjour";
Une fois de plus, ça te dérangerait de t'exprimer clairement. La
question était (autant qu'elle ait un sens comme l'a fait remarquer JMB) :
Je trouve que c'est assez clair, non ? Il suffit de compiler un
bout de code, et de regarder ce que ça donne...
Il est possible que je sois passé à côté de quelque chose, je ne suis
qu'un programmeur du dimanche et je manque en effet d'imagination, mais
je ne vois pas comment on peut accéder à un objet s'il n'est pas stocké
en mémoire.
Il est stocké en mémoire, certe, mais son adresse est, comment dire,
fantômatique :)
--
L'avis de décès de regroupement du groupe avec un groupe père pourrait
ainsi être publié post-mortem, évitant ainsi les votes désolants et
surtout les résurrections artificielles.
--{ fufe et le Vaudou }--
Non, si je fais du C, le compilateur pour moi est une boite noire, je ne m'intéresse pas à la façon dont le compilateur implémente telle ou telle fonctionnalité, chacun son boulot, pour moi le comportement visible du compilateur est défini par la norme et je n'ai pas à sortir de là,la
Qaund j'ai débuté ma carrière de programmeur du dimanche, je n'avais que le K&R1 et la doc du compilo (Metacomco C sur ST) pour apprendre. Je n'ai commancé qu'a bien appréhender les choses que quand je suis allé lire le code généré, et que j'ai essayé de refaire manuellement ce que faisais le compilateur. Bon, sur un 68k que l'on pratique déja bien, c'est plus facile qu'un i386 (et la syntaxe gnu :) mais c'est _très_ instructif.
Regarde le code machine qui resulte de char a[] = "bonjour"; char *b = "bonjour";
Une fois de plus, ça te dérangerait de t'exprimer clairement. La question était (autant qu'elle ait un sens comme l'a fait remarquer JMB) :
Je trouve que c'est assez clair, non ? Il suffit de compiler un bout de code, et de regarder ce que ça donne...
Il est possible que je sois passé à côté de quelque chose, je ne suis qu'un programmeur du dimanche et je manque en effet d'imagination, mais je ne vois pas comment on peut accéder à un objet s'il n'est pas stocké en mémoire.
Il est stocké en mémoire, certe, mais son adresse est, comment dire, fantômatique :)
-- L'avis de décès de regroupement du groupe avec un groupe père pourrait ainsi être publié post-mortem, évitant ainsi les votes désolants et surtout les résurrections artificielles. --{ fufe et le Vaudou }--
Thierry B.
--{ candide a plopé ceci: }--
Tiens, on dirait que je t'ai contaminé avec mes questions existentielles. :-))
Sûr et je suis en train de me demander ce que c'est que le C. Une branche de la métaphysique ? ;)
Parfois, oui. Il suffit de lire l'assembleur généré par un bon compilateur qui optimise bien pour avoir de vrais doutes :)
-- $ man 1 groff The grog command can be used to guess the correct groff command to use to format a file.
--{ candide a plopé ceci: }--
Tiens, on dirait que je t'ai contaminé avec mes questions
existentielles. :-))
Sûr et je suis en train de me demander ce que c'est que le C. Une
branche de la métaphysique ? ;)
Parfois, oui. Il suffit de lire l'assembleur généré par un bon
compilateur qui optimise bien pour avoir de vrais doutes :)
--
$ man 1 groff
The grog command can be used to guess the correct groff
command to use to format a file.
Tiens, on dirait que je t'ai contaminé avec mes questions existentielles. :-))
Sûr et je suis en train de me demander ce que c'est que le C. Une branche de la métaphysique ? ;)
Parfois, oui. Il suffit de lire l'assembleur généré par un bon compilateur qui optimise bien pour avoir de vrais doutes :)
-- $ man 1 groff The grog command can be used to guess the correct groff command to use to format a file.
Antoine Leca
Complément à ce qu'écrivit Jean-Marc...
En news:47dbde67$0$8167$, Francois va escriure:
int tab[10] ;
1) "tab " était égal à l'adresse du premier élément du tableau (l'élément 0). Cette adresse n'est pas connue au moment de la compilation car quand je fais
printf( "Valeur de tab %p", tab ) ;
Bzzzz. Le sujet est sorti il y a à peine un mois : news: (recoller l'adresse trop longue de Google si mon client l'a coupée), sujet « Adresse d'un tableau » daté 11 février, encore présent sur les groupes de Free à l'heure où j'écris ces lignes...
Là, tu liras que la forme correcte est plutôt
printf( "Valeur de tab %pn", (void*)tab ) ;
ce qui devrait te montrer assez clairement qu'au passage, il y a une conversion (ici explicite) entre le nom tab d'un côté, et la valeur passée comme paramètre à printf.
Pour info, la conclusion de ce fil est... qu'il n'est pas possible de récupérer la véritable adresse d'un tableau en C.
[ Cette adresse n'est pas connue au moment de la compilation car quand je fais printf(...) ]
j'obtiens une adresse différente à chaque exécution du programme.
Il n'y a pas de relation stricte entre les valeurs des adresses et ce que manipule le compilateur.
<EXPLICATION nécessaire="non" exacte="non"> Ton système reloge automatiquement le programme à chaque exécution, donc effectivement les adresses valsent. Cependant, le compilateur ne se base pas sur les adresses à l'exécution, mais plutôt sur les positions relatives des objets en mémoire (on appelle cela « code indépendant de la position », en anglais "position-independant code", PIC).
C'est l'objet de l'édition des liens (statique _et_ dynamique) de définir complètement les adresses. Et cette phase, plutôt complexe, sur ta machine est assurée par le système, indépendament en grande partie du compilateur au sens strict. </EXPLICATION>
Antoine
Complément à ce qu'écrivit Jean-Marc...
En news:47dbde67$0$8167$426a74cc@news.free.fr, Francois va escriure:
int tab[10] ;
1) "tab " était égal à l'adresse du premier élément du tableau
(l'élément 0). Cette adresse n'est pas connue au moment de la
compilation car quand je fais
printf( "Valeur de tab %p", tab ) ;
Bzzzz. Le sujet est sorti il y a à peine un mois :
news:67254dc4-1ac7-402e-9721-6da73c92e3fc@y5g2000hsf.googlegroups.com
(recoller l'adresse trop longue de Google si mon client l'a coupée), sujet
« Adresse d'un tableau » daté 11 février, encore présent sur les groupes de
Free à l'heure où j'écris ces lignes...
Là, tu liras que la forme correcte est plutôt
printf( "Valeur de tab %pn", (void*)tab ) ;
ce qui devrait te montrer assez clairement qu'au passage, il y a une
conversion (ici explicite) entre le nom tab d'un côté, et la valeur passée
comme paramètre à printf.
Pour info, la conclusion de ce fil est... qu'il n'est pas possible de
récupérer la véritable adresse d'un tableau en C.
[ Cette adresse n'est pas connue au moment de la
compilation car quand je fais printf(...) ]
j'obtiens une adresse différente à chaque exécution du programme.
Il n'y a pas de relation stricte entre les valeurs des adresses et ce que
manipule le compilateur.
<EXPLICATION nécessaire="non" exacte="non">
Ton système reloge automatiquement le programme à chaque exécution, donc
effectivement les adresses valsent. Cependant, le compilateur ne se base pas
sur les adresses à l'exécution, mais plutôt sur les positions relatives des
objets en mémoire (on appelle cela « code indépendant de la position », en
anglais "position-independant code", PIC).
C'est l'objet de l'édition des liens (statique _et_ dynamique) de définir
complètement les adresses. Et cette phase, plutôt complexe, sur ta machine
est assurée par le système, indépendament en grande partie du compilateur au
sens strict.
</EXPLICATION>
1) "tab " était égal à l'adresse du premier élément du tableau (l'élément 0). Cette adresse n'est pas connue au moment de la compilation car quand je fais
printf( "Valeur de tab %p", tab ) ;
Bzzzz. Le sujet est sorti il y a à peine un mois : news: (recoller l'adresse trop longue de Google si mon client l'a coupée), sujet « Adresse d'un tableau » daté 11 février, encore présent sur les groupes de Free à l'heure où j'écris ces lignes...
Là, tu liras que la forme correcte est plutôt
printf( "Valeur de tab %pn", (void*)tab ) ;
ce qui devrait te montrer assez clairement qu'au passage, il y a une conversion (ici explicite) entre le nom tab d'un côté, et la valeur passée comme paramètre à printf.
Pour info, la conclusion de ce fil est... qu'il n'est pas possible de récupérer la véritable adresse d'un tableau en C.
[ Cette adresse n'est pas connue au moment de la compilation car quand je fais printf(...) ]
j'obtiens une adresse différente à chaque exécution du programme.
Il n'y a pas de relation stricte entre les valeurs des adresses et ce que manipule le compilateur.
<EXPLICATION nécessaire="non" exacte="non"> Ton système reloge automatiquement le programme à chaque exécution, donc effectivement les adresses valsent. Cependant, le compilateur ne se base pas sur les adresses à l'exécution, mais plutôt sur les positions relatives des objets en mémoire (on appelle cela « code indépendant de la position », en anglais "position-independant code", PIC).
C'est l'objet de l'édition des liens (statique _et_ dynamique) de définir complètement les adresses. Et cette phase, plutôt complexe, sur ta machine est assurée par le système, indépendament en grande partie du compilateur au sens strict. </EXPLICATION>
Antoine
Antoine Leca
int toto[5]={0}; int titi[5]={0};
En news:47dc449e$0$17766$, candide va escriure:
Bien sûr. Oui mais JMB dit que l'objet tableau est la suite des entiers qu'il contient. Ci-dessus, toto et titi sont des tableaux de 5 éléments chacun et qui contiennent les 5 mêmes entiers
Non. Chaque entier (il y a en 10 en tout) sont des objets, et ils sont tous différents.
(à savoir que des 0).
Non et non.
D'abord, toto[0] et titi[0] sont techniquement différents des autres : les premiers sont initialisés avec la constante entière 0, alors que les autres sont initialisés avec la valeur par défaut. Il se trouve que la valeur de chacun des objets doit être égale à l'entier 0, mais cela ne suffit pas dans le cas général à décrire complètement le contenu de chacun des objets ; en particulier, un compilateur spéfiquement équipé pour repérer les variables non initialisées pourrait vouloir mettre un contenu différent dans les autres objets.
Ensuite, le fait que tous ces objets présentent la valeur 0 ne suffit pas à les rendre identiques : en effet, un objet en C est certes caractérisé par sa valeur ou plus largement son contenu, mais aussi par son adresse (et aussi par le type ayant servi à le décrire, mais là c'est un peu plus compliqué).
Antoine
int toto[5]={0};
int titi[5]={0};
En news:47dc449e$0$17766$426a74cc@news.free.fr, candide va escriure:
Bien sûr. Oui mais JMB dit que l'objet tableau est la suite des
entiers qu'il contient. Ci-dessus, toto et titi sont des tableaux de
5 éléments chacun et qui contiennent les 5 mêmes entiers
Non. Chaque entier (il y a en 10 en tout) sont des objets, et ils sont tous
différents.
(à savoir que des 0).
Non et non.
D'abord, toto[0] et titi[0] sont techniquement différents des autres : les
premiers sont initialisés avec la constante entière 0, alors que les autres
sont initialisés avec la valeur par défaut. Il se trouve que la valeur de
chacun des objets doit être égale à l'entier 0, mais cela ne suffit pas dans
le cas général à décrire complètement le contenu de chacun des objets ; en
particulier, un compilateur spéfiquement équipé pour repérer les variables
non initialisées pourrait vouloir mettre un contenu différent dans les
autres objets.
Ensuite, le fait que tous ces objets présentent la valeur 0 ne suffit pas à
les rendre identiques : en effet, un objet en C est certes caractérisé par
sa valeur ou plus largement son contenu, mais aussi par son adresse (et
aussi par le type ayant servi à le décrire, mais là c'est un peu plus
compliqué).
Bien sûr. Oui mais JMB dit que l'objet tableau est la suite des entiers qu'il contient. Ci-dessus, toto et titi sont des tableaux de 5 éléments chacun et qui contiennent les 5 mêmes entiers
Non. Chaque entier (il y a en 10 en tout) sont des objets, et ils sont tous différents.
(à savoir que des 0).
Non et non.
D'abord, toto[0] et titi[0] sont techniquement différents des autres : les premiers sont initialisés avec la constante entière 0, alors que les autres sont initialisés avec la valeur par défaut. Il se trouve que la valeur de chacun des objets doit être égale à l'entier 0, mais cela ne suffit pas dans le cas général à décrire complètement le contenu de chacun des objets ; en particulier, un compilateur spéfiquement équipé pour repérer les variables non initialisées pourrait vouloir mettre un contenu différent dans les autres objets.
Ensuite, le fait que tous ces objets présentent la valeur 0 ne suffit pas à les rendre identiques : en effet, un objet en C est certes caractérisé par sa valeur ou plus largement son contenu, mais aussi par son adresse (et aussi par le type ayant servi à le décrire, mais là c'est un peu plus compliqué).
Antoine
Antoine Leca
En news:, Jean-Marc Bourguet va escriure:
Erwan David writes:
Jean-Marc Bourguet écrivait :
Oops, j'aurais dû écrire,
L'objet toto c'est l'espace reservé pour ces 5 entiers.
l'objet n'a d'existence que dans le cadre de la machine abstraite qui défini le langage. Les 5 cases mémoires ne sont qu'une implémentation de cet objet par l'implémentation de C utilisée.
Pour quel objet est-ce que cette remarque n'est pas valable?
Ceux pour lesquels l'implémentation ne réserve en définitive pas d'espace ? (constante, variable reconnue comme inutile, etc.)
Antoine
En news:874pb6higq.fsf@news.bourguet.org, Jean-Marc Bourguet va escriure:
Erwan David writes:
Jean-Marc Bourguet écrivait :
Oops, j'aurais dû écrire,
L'objet toto c'est l'espace reservé pour ces 5 entiers.
l'objet n'a d'existence que dans le cadre de la machine abstraite qui
défini le langage. Les 5 cases mémoires ne sont qu'une
implémentation de cet objet par l'implémentation de C utilisée.
Pour quel objet est-ce que cette remarque n'est pas valable?
Ceux pour lesquels l'implémentation ne réserve en définitive pas d'espace ?
(constante, variable reconnue comme inutile, etc.)
L'objet toto c'est l'espace reservé pour ces 5 entiers.
l'objet n'a d'existence que dans le cadre de la machine abstraite qui défini le langage. Les 5 cases mémoires ne sont qu'une implémentation de cet objet par l'implémentation de C utilisée.
Pour quel objet est-ce que cette remarque n'est pas valable?
Ceux pour lesquels l'implémentation ne réserve en définitive pas d'espace ? (constante, variable reconnue comme inutile, etc.)
Antoine
Jean-Marc Bourguet
"Antoine Leca" writes:
En news:, Jean-Marc Bourguet va escriure:
Erwan David writes:
Jean-Marc Bourguet écrivait :
Oops, j'aurais dû écrire,
L'objet toto c'est l'espace reservé pour ces 5 entiers.
l'objet n'a d'existence que dans le cadre de la machine abstraite qui défini le langage. Les 5 cases mémoires ne sont qu'une implémentation de cet objet par l'implémentation de C utilisée.
Pour quel objet est-ce que cette remarque n'est pas valable?
Ceux pour lesquels l'implémentation ne réserve en définitive pas d'espace ? (constante, variable reconnue comme inutile, etc.)
Ca peut arriver pour des tableaux aussi :-)
A+
-- Jean-Marc FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc Site de usenet-fr: http://www.usenet-fr.news.eu.org
"Antoine Leca" <root@localhost.invalid> writes:
En news:874pb6higq.fsf@news.bourguet.org, Jean-Marc Bourguet va escriure:
Erwan David writes:
Jean-Marc Bourguet écrivait :
Oops, j'aurais dû écrire,
L'objet toto c'est l'espace reservé pour ces 5 entiers.
l'objet n'a d'existence que dans le cadre de la machine abstraite qui
défini le langage. Les 5 cases mémoires ne sont qu'une
implémentation de cet objet par l'implémentation de C utilisée.
Pour quel objet est-ce que cette remarque n'est pas valable?
Ceux pour lesquels l'implémentation ne réserve en définitive pas d'espace ?
(constante, variable reconnue comme inutile, etc.)
Ca peut arriver pour des tableaux aussi :-)
A+
--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org
L'objet toto c'est l'espace reservé pour ces 5 entiers.
l'objet n'a d'existence que dans le cadre de la machine abstraite qui défini le langage. Les 5 cases mémoires ne sont qu'une implémentation de cet objet par l'implémentation de C utilisée.
Pour quel objet est-ce que cette remarque n'est pas valable?
Ceux pour lesquels l'implémentation ne réserve en définitive pas d'espace ? (constante, variable reconnue comme inutile, etc.)
Ca peut arriver pour des tableaux aussi :-)
A+
-- Jean-Marc FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc Site de usenet-fr: http://www.usenet-fr.news.eu.org
Antoine Leca
En news:frh9si$5gu$, Marc Boyer va escriure:
On 2008-03-15, candide wrote:
D'accord mais alors comment l'implémentation fait-elle pour convertir implicitement un tableau en pointeur, faut bien qu'elle trouve l'adresse quelque part ?
Ben oui, le compilateur la calcule, mais ensuite, dans le code généré, il n'y a aucun besoin de la conserver.
Oui
Par exemple: double foo(void){ double p[10]; double q[10]; double r[10];
p[6]= 1.0; q[3]= 3.0; r[4]= 4.0; return p[6]+q[3]+r[4]; } Aucun besoin pour le compilo de laisser dans l'exécutable les adresses de début de p, q et r.
Certes.
Il génère trois accès en lecture/écriture sur trois adresses.
Non. Un compilateur normal va générer l'equivalent de
double foo(void){ return 8.0; }
éventuellement précédé du mot-clé inline.
Voire même, va supprimer totalement le code généré, s'il s'aperçoit qu'il n'y a aucun appel à la fonction foo dans tout le programme.
Dans le même genre [...]
risquent de donner lieu à exactement le même code,
Là encore, il est possible que tout ceci soit remplacé par
void bar(double d) {}
(voire supprimé complètement). Même si les compilateurs courants ne vont pas jusque là.
Antoine
En news:frh9si$5gu$1@news.cict.fr, Marc Boyer va escriure:
On 2008-03-15, candide wrote:
D'accord mais alors comment l'implémentation fait-elle pour convertir
implicitement un tableau en pointeur, faut bien qu'elle trouve
l'adresse quelque part ?
Ben oui, le compilateur la calcule, mais ensuite, dans
le code généré, il n'y a aucun besoin de la conserver.
Oui
Par exemple:
double foo(void){
double p[10];
double q[10];
double r[10];
p[6]= 1.0;
q[3]= 3.0;
r[4]= 4.0;
return p[6]+q[3]+r[4];
}
Aucun besoin pour le compilo de laisser dans l'exécutable
les adresses de début de p, q et r.
Certes.
Il génère trois accès en lecture/écriture sur trois adresses.
Non. Un compilateur normal va générer l'equivalent de
double foo(void){
return 8.0;
}
éventuellement précédé du mot-clé inline.
Voire même, va supprimer totalement le code généré, s'il s'aperçoit qu'il
n'y a aucun appel à la fonction foo dans tout le programme.
Dans le même genre
[...]
risquent de donner lieu à exactement le même code,
Là encore, il est possible que tout ceci soit remplacé par
void bar(double d) {}
(voire supprimé complètement). Même si les compilateurs courants ne vont pas
jusque là.
D'accord mais alors comment l'implémentation fait-elle pour convertir implicitement un tableau en pointeur, faut bien qu'elle trouve l'adresse quelque part ?
Ben oui, le compilateur la calcule, mais ensuite, dans le code généré, il n'y a aucun besoin de la conserver.
Oui
Par exemple: double foo(void){ double p[10]; double q[10]; double r[10];
p[6]= 1.0; q[3]= 3.0; r[4]= 4.0; return p[6]+q[3]+r[4]; } Aucun besoin pour le compilo de laisser dans l'exécutable les adresses de début de p, q et r.
Certes.
Il génère trois accès en lecture/écriture sur trois adresses.
Non. Un compilateur normal va générer l'equivalent de
double foo(void){ return 8.0; }
éventuellement précédé du mot-clé inline.
Voire même, va supprimer totalement le code généré, s'il s'aperçoit qu'il n'y a aucun appel à la fonction foo dans tout le programme.
Dans le même genre [...]
risquent de donner lieu à exactement le même code,
Là encore, il est possible que tout ceci soit remplacé par
void bar(double d) {}
(voire supprimé complètement). Même si les compilateurs courants ne vont pas jusque là.