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

Adresse d'un tableau

51 réponses
Avatar
candide
Bonjour,

Soit le programme suivant :

/*
* ---------------- adresseTableau.c -------------------
*/
#include <stdio.h>

int main(void)
{
int a[2] =3D { 100, 200 };

printf("%p\n%p\n", (void *) a, (void *) &a);

return 0;
}

/*
* -----------------------------------------------------------
*/

Il m'affiche deux valeurs identiques. Cette co=EFncidence d=E9pend-elle
exclusivement de l'impl=E9mentation ?

J'ai regard=E9 la norme et je ne vois rien qui permette de l'affirmer (=E0
la diff=E9rence de l'adresse d'une structure, cf. 6.7.2.1#13). Pourtant,
en consultant les archives de fclc, certains intervenants laissent
penser que oui. Pour clc, le discours est plus prudent mais aucune
r=E9ponse cat=E9gorique, comme si la question =E9tait mal pos=E9e.

La r=E9ponse la moins vague que j'aie pu trouver est celle de DMR sur
comp.std.c en 1999 qui dit :

/In the usual implementation the bit value of &a[0] and &a will be the
same,/

Comment faut-il interpr=E9ter ce /In the usual implementation/ ?

Merci pour toute clarification apport=E9e.

Candide

10 réponses

1 2 3 4 5
Avatar
Aris
Bonjour,

Soit le programme suivant :

/*
* ---------------- adresseTableau.c -------------------
*/
#include <stdio.h>

int main(void)
{
int a[2] = { 100, 200 };

printf("%pn%pn", (void *) a, (void *) &a);

return 0;
}

je pense que (void *) a est un raccourci pour &a[0]. et c'est un fait

que l'adresse d'un tableau est la même que l'adresse de son premier
element, du moins en C.
En c++, a et &a ne sont pas interchangeables parce qu'il n'y a plus
d'égalité entre const int * et int [].

Avatar
candide
On 11 fév, 15:19, Aris wrote:

et c'est un fait
que l'adresse d'un tableau est la même que l'adresse de son premier
element, du moins en C.


Tu te réfères à quoi pour dire cela ?

Avatar
Marc Boyer
On 2008-02-11, candide wrote:
Bonjour,

Soit le programme suivant :

/*
* ---------------- adresseTableau.c -------------------
*/
#include <stdio.h>

int main(void)
{
int a[2] = { 100, 200 };

printf("%pn%pn", (void *) a, (void *) &a);

return 0;
}

/*
* -----------------------------------------------------------
*/

Il m'affiche deux valeurs identiques. Cette coïncidence dépend-elle
exclusivement de l'implémentation ?

J'ai regardé la norme et je ne vois rien qui permette de l'affirmer (à
la différence de l'adresse d'une structure, cf. 6.7.2.1#13). Pourtant,
en consultant les archives de fclc, certains intervenants laissent
penser que oui. Pour clc, le discours est plus prudent mais aucune
réponse catégorique, comme si la question était mal posée.


C'est amusant comme question.
Je crois que je viens de trouver (grace à l'index: ça m'a bien
pris 20' cette histoire).
6.3.2.1/3
"Except when it is the operand of the sizeof operator /or the
unary & operator/ [...] an expression that has type "array of type"
is converted to an expression with type "pointer to type" that
points to the initial element of the array object".

On pourrait peut-être imaginer un compilateur hypothétique qui
stoque 'quelque chose' au début d'un tableau, avant le premier
élément (si j'ai pas raté un truc).

Dans la pratique, ce compilo casserait surement du code de
ci de là....

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
espie
In article ,
candide wrote:
Bonjour,

Soit le programme suivant :

/*
* ---------------- adresseTableau.c -------------------
*/
#include <stdio.h>

int main(void)
{
int a[2] = { 100, 200 };

printf("%pn%pn", (void *) a, (void *) &a);

return 0;
}

/*
* -----------------------------------------------------------
*/

Il m'affiche deux valeurs identiques. Cette coïncidence dépend-elle
exclusivement de l'implémentation ?

J'ai regardé la norme et je ne vois rien qui permette de l'affirmer (à
la différence de l'adresse d'une structure, cf. 6.7.2.1#13). Pourtant,
en consultant les archives de fclc, certains intervenants laissent
penser que oui. Pour clc, le discours est plus prudent mais aucune
réponse catégorique, comme si la question était mal posée.

La réponse la moins vague que j'aie pu trouver est celle de DMR sur
comp.std.c en 1999 qui dit :

/In the usual implementation the bit value of &a[0] and &a will be the
same,/


6.3.2.1 paragraphe 3 explique quoi faire avec une array, *sauf si celle-ci
est l'operande de sizeof ou du & unaire*.

6.5.3.2 paragraphe 3 done la semantique du & unaire, en particulier applique
a une array... sans plus de precision que de dire qu'il retourne l'adresse
du tableau...

Apres avoir fouille un peu plus, il semble bien qu'il n'y ait rien dans
la norme qui specifie que l'adresse d'un tableau doit etre egale a
l'adresse du premier element du tableau. L'exception mentionnee dans 6.3.2.1
laisse a penser que c'est intentionnel.

En pratique, je n'ai jamais rencontre d'implementation ou ca fait une
difference...

Mais comme on peut tres bien travailler avec le tableau lui-meme (qui
se transforme automatiquement en adresse du premier element), ca semble
etre un de ces cas ou ca n'apporte rien d'ecrire du code non portable ;-)

Avatar
Antoine Leca
En news:,
candide va escriure:
int a[2] = { 100, 200 };
printf("%pn%pn", (void *) a, (void *) &a);

Il m'affiche deux valeurs identiques. Cette coïncidence dépend-elle
exclusivement de l'implémentation ?


D'abord, la réponse technique mais sans intérêt à la question telle que tu
l'as posée.
C'est l'implémentation la plus commune, mais il est sûr que ce n'est pas la
seule normalisée. Dans la norme C (et Posix, et X/Open), %p est «
contraint » par une spécification très floue: la seule contrainte est qu'une
valeur convertie en écriture (*printf), si elle est relue (*scanf)
ultérieurement par le _même_ programme, doit donner un pointeur arbitraire
(void*) qui est « égal » (au sens de l'opération ==, donc la valeur binaire
peut être différente) à l'original.
À partir de là, on peut concevoir toute sorte de stupidités, y compris
écrire dans le printf("%p,...) l'heure à la suite du contenu binaire, la
seule condition de conformité étant que l'opération scanf("%p") sache y
retrouver ses petits.


Pour la suite, je suppose que tu te proposes de comparer a et &a.
La différence essentielle en est (comme dit S. Summit dans sa FAQ, §6.12,
http://c-faq.com/aryptr/aryvsadr.html) « le type ».

À partir de là, comme rien n'oblige deux types pointeurs (non compatibles) à
partager la même représentation binaire, il devrait être clair que tu peux
avoir deux formes totalement différentes ; notament tu peux avoir (cela
s'est vu) une implémentation où a, ou plutôt &a[0], soit d'une taille
(sizeof) plus grande que &a !

Une autre question est la /comparaison/ entre les valeurs. Du point de vue
du C, il y a deux cas différents, les opérateurs d'ordre (6.5.8) et ceux
d'égalité (6.5.9). Dans le premier cas, les deux pointeurs doivent être vers
des types compatibles, ce qui imposent des transtypages explicites des deux
côtés, et la représentation peut changer (6.3, en particulier 6.3p2) dans de
grandes proportions ; je ne pense pas que l'on puisse en tirer quoi que ce
soit d'utile.

Pour le cas des opérateurs d'égalité, il est aussi possible de comparer un
pointeur arbitraire (au hasard, &a !) avec un pointeur universel void*; dans
ton exemple cela imposerait un transtypage avec conversion de int* vers
void*, mais si on prend le cas peu différent de char b[2], on peut faire
abstraction de cette conversion supplémentaire. On en réduit le champ à
6.5.9p6, qui est le plus précis (que j'ai trouvé) que la norme puisse
t'offrir :
Two pointers compare equal if and only if [...] both are pointers to
the
same object (including a pointer to an object and a subobject at its
beginning) [...]

En clair, avec
char b[2] = { 100, 200 };
void* p = b; /* sans effet de conversion, d'après 6.2.5p27 */
on a
p == &b

Rappel : les valeurs binaires peuvent être très différentes !


La réponse la moins vague que j'aie pu trouver est celle de DMR sur
comp.std.c en 1999 qui dit :

/In the usual implementation the bit value of &a[0] and &a will be the
same,/

Comment faut-il interpréter ce /In the usual implementation/ ?


Intéressante référence ; pour le contexte,
http://groups.google.com/groups" target="_blank" class="text-blue hover:opacity-90 " style="word-break: break-all;" rel="noopener nofollow">http://groups.google.com/groups? ; dans
la même enfilade, Larry Jones, par ailleurs membre du comité et celui qui
écrit le texte de la norme, écrivait dans
http://groups.google.com/groups" target="_blank" class="text-blue hover:opacity-90 " style="word-break: break-all;" rel="noopener nofollow">http://groups.google.com/groups? que «
l'adresse est "la même" que celle du premier élément »...
En 2004, le sujet est ressorti une fois de plus
(http://groups.google.com/group/comp.std.c/browse_thread/thread/5533e861f117
55e0); il est intéressant de noter que la position de Larry (en réponse à
Dan Pop) est devenue moins nette...

Pour moi, ce /In the usual implementation/ signifie que tu ne peux pas te
fier à ce que les valeurs binaires (celles que l'on obtient avec memcpy()
vers des tableaux de unsigned char) soient identiques.

D'ailleurs, les représentations des pointeurs d'un type donné ne sont pas
forcément univoques (et on connait plusieurs implémentations où elles sont
effectivement variables, donc où l'opération == sur les pointeurs n'est pas
aussi simple qu'une comparaison de char). Et ce, indépendament de savoir si
ce sont des pointeurs vers tableaux ou pas.


De toutes les façons, je ne vois pas bien l'intérêt de chercher à savoir si
la représentation d'un pointeur vers tableau (qui est un type exotique en C)
est ou pas la même que celle d'une autre sorte de pointeur, quand bien même
il s'agirait d'un objet du même tableau. Si tu ne veux pas de souci, tu dois
de toutes manières éviter le plus possible d'utiliser &a (cf. aussi la
question 6.13 de la FAQ anglaise, celle qui suit la référence ci-dessus.)
Et je rejoins ici la conclusion de Marc Espie (en faisant largement plus
long)!


Antoine

Avatar
Antoine Leca
En news:, Marc Boyer va escriure:
On pourrait peut-être imaginer un compilateur hypothétique qui
stoque 'quelque chose' au début d'un tableau, avant le premier
élément (si j'ai pas raté un truc).


Rigolo comme idée.
J'ai examiné plusieurs facettes (y compris la construction des structures,
6.7.2.1p13 en particulier, 6.2.5p20 ou 6.5.6p8), et je n'ai pas trouvé non
plus de motifs pour rejeter cela /a priori/. Il faudrait seulement des
versions spéciales pour == (6.5.9) ou sizeof (6.5.3.4) qui ferait
abstraction du « quelque chose ».


On peut aussi imaginer une implémentation où les tableaux seraient en fait
des descripteurs de tableaux, en gros une structure stockée «ailleurs», avec
entre autres un pointeur vers les véritables données. Dans ces conditions,
&a retournerait l'adresse de ce descripteur, qui serait incommensurable
(sauf conversion) avec les données elles-mêmes. Il est possible de glisser
de tels descripteurs /derrière/ le tableau dans une structure, par exemple
(6.7.2.1p15). Un des intérêts de ce genre d'implémentation serait de pouvoir
implémenter une vérification dynamique des bornes des tableaux.
Je n'ai pas vérifié en profondeur, mais je pense que cela doit être conforme
(moyennant une pelleté de restrictions d'implémentations à documenter.)


Dans la pratique, ce compilo casserait surement du code de
ci de là....


Oui, mais le code en question est probablement mal écrit. Les raisons
d'utiliser &a sont normalement très rares, en général liées à l'utilisation
de tableaux « multi-dimensionnels », et le code résultant est très souvent
incorrect du point de vue de la norme (suppose un adressage linéaire)... Et
quand le code est correct, je ne pense pas que les valeurs soient jamais
comparées à d'autres types, donc les soucis ne devraient pas apparaître !


Antoine

Avatar
Vincent Lefevre
Dans l'article <forstf$svr$,
Antoine Leca écrit:

Oui, mais le code en question est probablement mal écrit. Les raisons
d'utiliser &a sont normalement très rares, en général liées à l'utilisation
de tableaux « multi-dimensionnels », et le code résultant est très souvent
incorrect du point de vue de la norme (suppose un adressage linéaire)... Et
quand le code est correct, je ne pense pas que les valeurs soient jamais
comparées à d'autres types, donc les soucis ne devraient pas apparaître !


Y a-t-il des outils qui vérifient si la forme &a (où a est un tableau)
est utilisée? splint ne semble pas le faire.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/&gt;
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/&gt;
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)

Avatar
Marc Boyer
On 2008-02-12, Antoine Leca wrote:
Pour le cas des opérateurs d'égalité, il est aussi possible de comparer un
pointeur arbitraire (au hasard, &a !) avec un pointeur universel void*; dans
ton exemple cela imposerait un transtypage avec conversion de int* vers
void*, mais si on prend le cas peu différent de char b[2], on peut faire
abstraction de cette conversion supplémentaire. On en réduit le champ à
6.5.9p6, qui est le plus précis (que j'ai trouvé) que la norme puisse
t'offrir :
Two pointers compare equal if and only if [...] both are pointers to
the
same object (including a pointer to an object and a subobject at its
beginning) [...]

En clair, avec
char b[2] = { 100, 200 };
void* p = b; /* sans effet de conversion, d'après 6.2.5p27 */


Pardon, est-ce que tes doigts n'ont pas fourché sur le clavier.
Parce que dans ce que j'ai sous les yeux, 6.2.5p27, c'est un exemple
sur float* et const float *.

on a
p == &b

Rappel : les valeurs binaires peuvent être très différentes !


Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)

Avatar
Vincent Lefevre
Dans l'article ,
Marc Boyer écrit:

On 2008-02-12, Antoine Leca wrote:
En clair, avec
char b[2] = { 100, 200 };
void* p = b; /* sans effet de conversion, d'après 6.2.5p27 */


Pardon, est-ce que tes doigts n'ont pas fourché sur le clavier.
Parce que dans ce que j'ai sous les yeux, 6.2.5p27, c'est un exemple
sur float* et const float *.


C'est le cas dans n1124 (basée sur le TC2) et ce dont parle Antoine
est le 6.2.5p26, mais dans n869 (dernier draft public de C99), ça
correspond bien. La confusion vient peut-être de là.

--
Vincent Lefèvre - Web: <http://www.vinc17.org/&gt;
100% accessible validated (X)HTML - Blog: <http://www.vinc17.org/blog/&gt;
Work: CR INRIA - computer arithmetic / Arenaire project (LIP, ENS-Lyon)


Avatar
Marc Boyer
On 2008-02-12, Antoine Leca wrote:
En news:, Marc Boyer va escriure:
On pourrait peut-être imaginer un compilateur hypothétique qui
stoque 'quelque chose' au début d'un tableau, avant le premier
élément (si j'ai pas raté un truc).


Rigolo comme idée.
J'ai examiné plusieurs facettes (y compris la construction des structures,
6.7.2.1p13 en particulier, 6.2.5p20 ou 6.5.6p8), et je n'ai pas trouvé non
plus de motifs pour rejeter cela /a priori/. Il faudrait seulement des
versions spéciales pour == (6.5.9) ou sizeof (6.5.3.4) qui ferait
abstraction du « quelque chose ».


Pour sizeof (6.5.3.4p6) je vois bien le problème. Encore que, tant
que le quelque chose est < sizeof array[0], de part la division entre
entiers, ça reste valide ;-)

On peut aussi imaginer une implémentation où les tableaux seraient en fait
des descripteurs de tableaux, en gros une structure stockée «ailleurs», avec
entre autres un pointeur vers les véritables données. Dans ces conditions,
&a retournerait l'adresse de ce descripteur, qui serait incommensurable
(sauf conversion) avec les données elles-mêmes. Il est possible de glisser
de tels descripteurs /derrière/ le tableau dans une structure, par exemple
(6.7.2.1p15). Un des intérêts de ce genre d'implémentation serait de pouvoir
implémenter une vérification dynamique des bornes des tableaux.
Je n'ai pas vérifié en profondeur, mais je pense que cela doit être conforme
(moyennant une pelleté de restrictions d'implémentations à documenter.)


Le problème, c'est que je vois pas comment ce genre d'implantation
pourra faire tourner un code de copie à base de memcpy:

int a[5]= { 0 , 1 , 2 , 3 , 4 };
int b[20];
memcpy(&b, &a, sizeof a );
assert( a[0] == b[0] );

car il va être difficile à memcpy (qui ne verra que le descripteur,
par conversion int[5]* en int* puis void* ) d'aller copier les vrais
valeurs.

Ca le sera aussi d'ailleurs avec ma proposition d'un petit truc en
plus devant.

Après, pour une vérification dynamique, je serais tenté de mettre
des choses après ou avant le tableau, mais en regardant 6.5.9p6 et la
note de bas de page 91, je me demande si on a le droit.

Dans la pratique, ce compilo casserait surement du code de
ci de là....


Oui, mais le code en question est probablement mal écrit. Les raisons
d'utiliser &a sont normalement très rares, en général liées à l'utilisation
de tableaux « multi-dimensionnels », et le code résultant est très souvent
incorrect du point de vue de la norme (suppose un adressage linéaire)... Et
quand le code est correct, je ne pense pas que les valeurs soient jamais
comparées à d'autres types, donc les soucis ne devraient pas apparaître !


En effet.


Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)


1 2 3 4 5