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

2 3 4 5 6
Avatar
Marc Boyer
On 2008-02-12, Antoine Leca wrote:
En news:, Marc Boyer va escriure:
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 */


AMHA, il y conversion en int* ici (6.3.2.1p3) puis int* -> void*.


Tu as sauté un épisode ! C'est justement pour éviter ces conversions
intermédiaires que j'ai changé les int initiaux en char, et remplacer a par
b ! ;-)


Oui, en effet.

on a
p == &b


Mais est-ce que 'p' pointe 'at its beginning' ?


Je crois (après avoir lu ton autre message) que c'est préférable si l'on
veut que memcpy() fonctionne...


Oui, on est d'accord, ce serait mieux.

Maintenant, je ne sais pas démontrer que c'est garanti par la norme. Il y a
un flou entre 6.2.5p27 (même représentation pour void* et char*), 6.3.2.3p1
(conversion vers et depuis void*) et 6.5.6p8 (égalité de pointeurs) qui ne
me permettent pas d'éclaircir toutes les alternatives.
En d'autres termes, je ne suis pas sûr que ton exemple

#include <string.h>
#include <assert.h>
int main()
{
static const int a[5]= { 10 , -9 , 8 , -7 , 6 };
int b[20];
memcpy(&b, &a, sizeof a );
assert( a[0] == b[0] );
return 0;
}

soit strictement conforme _et_ muet.


Comme un sujet analogue court sur comp.std.c (à partir d'une remarque dans
news:97aa3$47afd2a8$ca8010a3$, Larry Jones a lancé
news: sur le même sujet de &array), j'ai
glissé une référence vers notre discussion et nos questions, peut-être que
les gourous et autres sorciers vont donner des réponses plus précises à nos
interrogations.


J'ai vu ça. Wait and see.

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
Erwan David
Vincent Lefevre <vincent+ écrivait :

AMHA, on a forcément (void*) a == (void*) &a, i.e. l'objet tableau
doit débuter sur son premier élément a[0], enfin tout du moins
c'est ce qui doit être visible pour l'utilisateur. Je ne sais pas
si la norme le dit explicitement. Mais si on suppose que l'égalité
n'est pas forcément respectée parce que l'objet tableau peut contenir
des données avant son premier élément, alors plein de choses deviennent
incorrectes dans la norme. Par exemple:


D'un autre côté quand je fais un malloc(sizeof(type)*numElements) il y a
très souvent des données avant le pointeur retourné. Ne serait-ce que
des données utilisées par l'allocateur, pour pouvoir effectuer free().

--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé

Avatar
Antoine Leca
En news:, Marc Boyer va escriure:
On 2008-02-12, Antoine Leca wrote:
memcpy(&b, &a, sizeof a );



OK. Disons que le <string.h> était implicite dans mon post.

Si tu as explicité le prototype, ou si tu as #inclut <string.h>, tu
as écris en fait

memcpy((void*)&b, (void*)&a, sizeof a );

et circulez, il n'y a plus de descripteur à voir...


Je suppose que, formellement, il faut faire le cast en void* avant
un appel à memcpy, c'est ça ?


Non non, l'objet des prototypes c'est justement de rajouter la conversion
*sans* que le programmeur soit obligé de rajouter les hideux transtypages
(casts).


C'est vrai que je ne le fais jamais... Honte à moi...


Si tu écris du code sans prototype (ce qui m'étonnerait), effectivement
honte à toi. ;-)

Maintenant, avec les compilateurs anciens (et tous ceux qui sont
compatibles), tous les pointeurs sont compatibles entre eux et il n'y a pas
de problème dans la pratique.
Mais c'est vrai que ceux qui ont travaillés avec des environnements ayant
plusieurs représentations possibles pour les pointeurs (au hasard, x86-16
:o) ) savent ce qui arrive lorsqu'on oublie un transtypage, et pourquoi il y
a un avertissement pour rappeler que l'absence de prototype cache
probablement une possibilité d'erreur...

La question de savoir si c'est dommage ou pas que ce ne soit plus un
problème dans la pratique est... bof non, ce n'est pas une bonne question !
:-D


Antoine



Avatar
Marc Boyer
On 2008-02-12, Antoine Leca wrote:
En news:, Marc Boyer va escriure:
On 2008-02-12, Antoine Leca wrote:
memcpy(&b, &a, sizeof a );



OK. Disons que le <string.h> était implicite dans mon post.

Si tu as explicité le prototype, ou si tu as #inclut <string.h>, tu
as écris en fait

memcpy((void*)&b, (void*)&a, sizeof a );

et circulez, il n'y a plus de descripteur à voir...


Je suppose que, formellement, il faut faire le cast en void* avant
un appel à memcpy, c'est ça ?


Non non, l'objet des prototypes c'est justement de rajouter la conversion
*sans* que le programmeur soit obligé de rajouter les hideux transtypages
(casts).


Non, OK.
Donc, ce que tu me disais, c'est qu'en fait, il y a un (void*)
implicite, et que donc, l'adresse du descripteur (&a) est transformée
en (void*)&a donc en l'adresse réelle des données.


C'est vrai que je ne le fais jamais... Honte à moi...


Si tu écris du code sans prototype (ce qui m'étonnerait), effectivement
honte à toi. ;-)


Non, je me suis demandé un instant si la conversion en void* n'était
pas implicite. Et comme je n'ai pas réussi à la retrouver dans la norme,
je me suis dit que j'avais raté un truc.

[SNIP, vieilleries]
La question de savoir si c'est dommage ou pas que ce ne soit plus un
problème dans la pratique est... bof non, ce n'est pas une bonne question !


En effet. A moins que ça le re-devienne un jour, mais ce que je
comprend de l'archi des processeurs actuels va plutôt vers des
registres qui peuvent adresser plus de mémoire que ce qu'on arrive
à mettre dans la machine.

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 ,
Erwan David écrit:

Vincent Lefevre <vincent+ écrivait :

AMHA, on a forcément (void*) a == (void*) &a, i.e. l'objet tableau
doit débuter sur son premier élément a[0], enfin tout du moins
c'est ce qui doit être visible pour l'utilisateur. Je ne sais pas
si la norme le dit explicitement. Mais si on suppose que l'égalité
n'est pas forcément respectée parce que l'objet tableau peut contenir
des données avant son premier élément, alors plein de choses deviennent
incorrectes dans la norme. Par exemple:


D'un autre côté quand je fais un malloc(sizeof(type)*numElements) il y a
très souvent des données avant le pointeur retourné. Ne serait-ce que
des données utilisées par l'allocateur, pour pouvoir effectuer free().


Oui, mais l'utilisateur ne voit pas ces données.

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


Avatar
espie
In article <20080212152318$,
Vincent Lefevre <vincent+ wrote:
Dans l'article ,
Marc Boyer écrit:

Dans mon idée, justement non.
On part sur la question: est-ce que
(void*) a == (void*) &a
et on essaye de voir si la norme laisse (intentionnellement ou non)
la place pour une implantation conforme dans laquelle
(void*) a != (void*) &a
sachant qu'on a déjà bien sûr
(void*) a == (void*) (&a[0])


AMHA, on a forcément (void*) a == (void*) &a, i.e. l'objet tableau
doit débuter sur son premier élément a[0], enfin tout du moins
c'est ce qui doit être visible pour l'utilisateur.


Non, la norme n'est pas d'accord. La norme ne te dit pas ce que doit donner
&a, ou plus exacement comme prendre l'adresse d'un tableau.

Elle est tres precise sur le fait qu'un tableau `degenere' en pointeur
sur le premier element, elle est egalement tres precise sur la taille
totale d'un tableau (sizeof), elle ne mentionne juste pas ce que ca veut
dire que prendre l'adresse d'un tableau, et pire, elle indique ce cas-la
comme un des deux cas exceptionnels avec sizeof, ou le tableau n'est pas
equivalent a un pointeur sur le premier element.

C'est louche, c'est sans doute un trou dans la norme, je ne vois pas trop
qu'est-ce qu'on peut en attendre comme comportement sense ou utile, mais
c'est comme ca...


Avatar
Vincent Lefevre
Dans l'article <fosth4$16iu$,
Marc Espie écrit:

In article <20080212152318$,
Vincent Lefevre <vincent+ wrote:
AMHA, on a forcément (void*) a == (void*) &a, i.e. l'objet tableau
doit débuter sur son premier élément a[0], enfin tout du moins
c'est ce qui doit être visible pour l'utilisateur.


Non, la norme n'est pas d'accord. La norme ne te dit pas ce que doit
donner &a, ou plus exacement comme prendre l'adresse d'un tableau.


Si. Le tableau est un objet et &a pointe sur cet objet (6.5.3.2#3),
i.e. le premier multiplet de cet objet (6.3.2.3#7). Ensuite le
problème est de savoir s'il peut y avoir des données avant le premier
élément du tableau a[0]. La norme n'est pas explicite sur ce point,
mais des exemples dans 6.5.3.4 montrent que la réponse est non.

Ce que la norme ne dit pas, c'est comment sont représentés les
pointeurs en mémoire.

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


Avatar
espie
In article <20080212232405$,
Vincent Lefevre <vincent+ wrote:
Dans l'article <fosth4$16iu$,
Marc Espie écrit:

In article <20080212152318$,
Vincent Lefevre <vincent+ wrote:
AMHA, on a forcément (void*) a == (void*) &a, i.e. l'objet tableau
doit débuter sur son premier élément a[0], enfin tout du moins
c'est ce qui doit être visible pour l'utilisateur.


Non, la norme n'est pas d'accord. La norme ne te dit pas ce que doit
donner &a, ou plus exacement comme prendre l'adresse d'un tableau.


Si. Le tableau est un objet et &a pointe sur cet objet (6.5.3.2#3),
i.e. le premier multiplet de cet objet (6.3.2.3#7). Ensuite le


Je ne suis pas d'accord, ca n'est pas ce qui est ecrit dans la norme.
6.5.3.2#3 te dit effectivement que &a retourne l'adresse de a, et plus
precisemment un pointeur vers l'objet a.

Et 6.3.2.3#7 te dit que le pointeur vers a peut etre converti en pointeur
vers un type different, et reconverti vers le pointeur de depart.

Mais il ne te dit rien sur ce que doit etre le pointeur de depart avant
conversion.



Avatar
Vincent Lefevre
Dans l'article <fotb3m$1bko$,
Marc Espie écrit:

In article <20080212232405$,
Vincent Lefevre <vincent+ wrote:
Si. Le tableau est un objet et &a pointe sur cet objet (6.5.3.2#3),
i.e. le premier multiplet de cet objet (6.3.2.3#7). Ensuite le


Je ne suis pas d'accord, ca n'est pas ce qui est ecrit dans la norme.
6.5.3.2#3 te dit effectivement que &a retourne l'adresse de a, et plus
precisemment un pointeur vers l'objet a.

Et 6.3.2.3#7 te dit que le pointeur vers a peut etre converti en pointeur
vers un type different, et reconverti vers le pointeur de depart.


Et il dit plus que ça. En particulier:

When a pointer to an object is converted to a pointer to a
character type, the result points to the lowest addressed byte of
the object.

Je rappelle ce qu'on cherchait à montrer:

(void*) a == (void*) &a

Comme l'égalité ci-dessus est équivalente à

&a[0] == &a

alors alternativement, on peut utiliser 6.5.9#6 (opérateur d'égalité,
sur les pointeurs):

Two pointers compare equal if and only if both are null pointers,
both are pointers to the same object (including a pointer to an
object and a subobject at its beginning)

On est ici dans le cas d'un pointeur vers un objet (le tableau a) et
d'un pointeur vers un sous-objet à son début (l'élément a[0]).

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


Avatar
Antoine Leca
En news:20080212235426$, Vincent Lefevre va
escriure:
Je rappelle ce qu'on cherchait à montrer:

(void*) a == (void*) &a

Comme l'égalité ci-dessus est équivalente à

&a[0] == &a


Pas d'accord.
En supposant int a[XXX], le pointeur de gauche est de type int*, celui de
droite de type int(*)[XXX], je n'ai pas trouvé dans la norme de texte
définissant explicitement le comportement de l'opération == ci-dessus dans
un tel cas.

Pour pouvoir arriver à quelque chose, il faut faire intervenir des
conversions supplémentaires, et on revient pratiquement à l'expression du
début. Donc on ne peut pas « simplifier » autant.


Cela étant, il n'est pas nécessaire de simplifier pour pouvoir appliquer
6.5.9, bien au contraire.
Seulement, savoir que les pointeurs convertis soient égaux au sens de
l'opération == n'apporte pas beaucoup d'information sur les pointeurs avant
conversion.


Antoine

2 3 4 5 6