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
Marc Boyer
On 2008-02-12, Vincent Lefevre <vincent+ wrote:
Dans l'article ,
Marc Boyer écrit:

On 2008-02-12, Antoine Leca wrote:
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] );


Aucun problème puisque cette implémentation s'occupe de faire les
transformations nécessaires de façon à ce que sémantiquement, &a
pointe sur a[0].


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])

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
Antoine Leca
En news:, Marc Boyer va escriure:
On 2008-02-12, Antoine Leca wrote:
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.


Huh ?
Si tu n'as pas inclus de prototypes pour memcpy(), le comportement de ton
code n'est pas défini (6.5.2.2p6, dernière phrase, tu passes un pointeur
vers tableau qui n'est pas à ma connaissance compatible avec un void*).

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...



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.


Où est le problème ? Il est évident que les descripteurs sont des éléments
de remplissage au sens de la norme.

(Marc, tu devrais changer ton lien de référence pour N1256: note 94)


Antoine


Avatar
Erwan David
Jean-Marc Bourguet écrivait :

"Antoine Leca" writes:

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 ».


Je ne vois pas comment faire fonctionner quelque chose comme

int (*a)[5];
a = malloc(sizeof a);


Moi c'est plutôt

type[] a;

a=malloc(sizeof(type)*numElements);

puis utilisation de a comme tableau de numElements elements que je vois
mal passer s'il y a un truc caché dans les tableaux.

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



Avatar
Marc Boyer
On 2008-02-12, Vincent Lefevre <vincent+ wrote:
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 */


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


Que vient faire ici le int* ? C'est un char*. Mais un char* et un
void* ont la même représentation:


Pardon, erreur de lecture de ma part.

[citation du draft n869]
[#27] A pointer to void shall have the same representation
and alignment requirements as a pointer to a character type.

on a
p == &b


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


La norme dit: "that points to the initial element of the array object".
C'est pareil, non?


Non, non, 6.5.9p6 dit "at its beginning"... Oui, je pinaille.
En gros, est-ce qu'on pourrait avoir du padding au début d'un tableau ?
Je pense que oui, ssi il est d'une taille < à la taille d'un élément.

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
Antoine Leca
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 ! ;-)


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...


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.


Antoine


Avatar
Vincent Lefevre
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. 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:

[#7] EXAMPLE 3 In this example, the size of a variable-
length array is computed and returned from a function:

size_t fsize3 (int n)
{
char b[n+3]; // Variable length array.
return sizeof b; // Execution time sizeof.
}
int main()
{
size_t size;
size = fsize3(10); // fsize3 returns 13.
return 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
Marc Boyer
On 2008-02-12, Antoine Leca wrote:
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.


Huh ?
Si tu n'as pas inclus de prototypes pour memcpy(), le comportement de ton
code n'est pas défini (6.5.2.2p6, dernière phrase, tu passes un pointeur
vers tableau qui n'est pas à ma connaissance compatible avec un void*).


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 ?
C'est vrai que je ne le fais jamais... Honte à moi...

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:

Non, non, 6.5.9p6 dit "at its beginning"... Oui, je pinaille.
En gros, est-ce qu'on pourrait avoir du padding au début d'un tableau ?
Je pense que oui, ssi il est d'une taille < à la taille d'un élément.


Les tableaux ne semblent pas pouvoir avoir de padding:

6.5.3.4 The sizeof operator
[...]
[#3] When applied to an operand that has type char, unsigned
char, or signed char, (or a qualified version thereof) the
result is 1. When applied to an operand that has array
type, the result is the total number of bytes in the
array.75) When applied to an operand that has structure or
union type, the result is the total number of bytes in such
an object, including internal and trailing padding.

Tu remarqueras que la norme parle de padding pour les structures,
mais est silencieuse pour les tableaux. Un peu plus loin:

[#6] EXAMPLE 2 Another use of the sizeof operator is to
compute the number of elements in an array:

sizeof array / sizeof array[0]

Cet exemple deviendrait faux s'il y avait du padding. Et j'ai aussi
cité l'exemple 3 dans un article précédent.

--
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
Marc Boyer
On 2008-02-12, 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. 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:

[#7] EXAMPLE 3 In this example, the size of a variable-
length array is computed and returned from a function:

size_t fsize3 (int n)
{
char b[n+3]; // Variable length array.
return sizeof b; // Execution time sizeof.
}
int main()
{
size_t size;
size = fsize3(10); // fsize3 returns 13.
return 0;
}


A oui, je l'avais raté celui là.
Donc, en effet, ma proposition ne tient pas. Ceci dit, c'est plutôt
une bonne chose ;-)

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
Marc Boyer
On 2008-02-12, Vincent Lefevre <vincent+ wrote:
Dans l'article ,
Marc Boyer écrit:

Non, non, 6.5.9p6 dit "at its beginning"... Oui, je pinaille.
En gros, est-ce qu'on pourrait avoir du padding au début d'un tableau ?
Je pense que oui, ssi il est d'une taille < à la taille d'un élément.


Les tableaux ne semblent pas pouvoir avoir de padding:

6.5.3.4 The sizeof operator
[...]
[#3] When applied to an operand that has type char, unsigned
char, or signed char, (or a qualified version thereof) the
result is 1. When applied to an operand that has array
type, the result is the total number of bytes in the
array.75) When applied to an operand that has structure or
union type, the result is the total number of bytes in such
an object, including internal and trailing padding.

Tu remarqueras que la norme parle de padding pour les structures,
mais est silencieuse pour les tableaux.


Oui, sur l'intention, je suis d'accord. Mais formellement, je me
posais la question.

Un peu plus loin:

[#6] EXAMPLE 2 Another use of the sizeof operator is to
compute the number of elements in an array:

sizeof array / sizeof array[0]

Cet exemple deviendrait faux s'il y avait du padding.


Sauf si ce padding est petit (<sizeof array[0]) à cause
des règles de divisions entre entiers. C'est dans ce petit interstice
que je me demandais s'il y avait un pb.

Et j'ai aussi
cité l'exemple 3 dans un article précédent.


Qui invalide complètement mon hypothèse. Ouf...

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