J'ai trouvé une fonction qui calcule le carré d'un nombre:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
Il renvoie la valeur n 2 . La question est, comment fait-il cela? Après quelques tests, j'ai trouvé qu'entre (&a)[k]
et (&a)[k+1]
est sizeof(a)
/ sizeof(int)
. Pourquoi donc?
int p(n)
? Cela compile-t-il même?int q(int n) { return sizeof (char [n][n]); }
sizeof
était pour enregistrer des caractères. Tout le monde: c'est du code intentionnellement obscur, c'est un comportement indéfini, la réponse de @ ouah est correcte.Réponses:
Evidemment un hack ... mais une façon de quadriller un nombre sans utiliser l'
*
opérateur (c'était une exigence du concours de codage).équivaut à un pointeur vers un
int
emplacementet donc l'expression entière est
la source
(&a)
comme un pointeur vers un objet dont len*sizeof(int)
momentn
n'est pas connu au moment de la compilation. C était un langage simple ...Pour comprendre ce hack, vous devez d'abord comprendre la différence de pointeur, c'est-à-dire que se passe-t-il lorsque deux pointeurs pointant vers des éléments du même tableau sont soustraits?
Lorsqu'un pointeur est soustrait d'un autre, le résultat est la distance (mesurée en éléments du tableau) entre les pointeurs. Donc, si
p
pointe versa[i]
etq
pointe versa[j]
, alorsp - q
est égal ài - j
.C11: 6.5.6 Opérateurs additifs (p9):
Maintenant, je m'attends à ce que vous soyez conscient de la conversion du nom du tableau en pointeur,
a
convertit en pointeur vers le premier élément du tableaua
.&a
est l'adresse du bloc mémoire entier, c'est-à-dire que c'est une adresse de tableaua
. La figure ci-dessous vous aidera à comprendre ( lisez cette réponse pour une explication détaillée ):Cela vous aidera à comprendre pourquoi
a
et&a
a la même adresse et comment(&a)[i]
est l'adresse du i ème tableau (de même taille que celle dea
).Donc, la déclaration
est équivalent à
et cette différence donnera le nombre d'éléments entre les pointeurs
(&a)[n]
et(&a)[0]
, qui sont desn
tableaux de chacun desn
int
éléments. Par conséquent, le total des éléments du tableau estn*n
=n
2 .REMARQUE:
C11: 6.5.6 Opérateurs additifs (p9):
Puisque
(&a)[n]
ni ne pointe vers des éléments du même objet de tableau ni un après le dernier élément de l'objet de tableau,(&a)[n] - a
invoquera un comportement indéfini .Notez également que, mieux vaut changer le type de retour de la fonction
p
enptrdiff_t
.la source
&a[k]
est une adresse duk
e élément du tableaua
. C'est(&a)[k]
cela qui sera toujours considéré comme l'adresse d'un tableau d'k
éléments. Ainsi, le premier élément est à la positiona
(ou&a
), le second est à la positiona
+ (nombre d'éléments du tableaua
qui estn
) * (taille d'un élément du tableau) et ainsi de suite. Et notez que la mémoire pour les tableaux de longueur variable est allouée sur la pile, pas sur le tas.a
est un tableau (variable) den
int
.&a
est un pointeur vers un tableau (variable) den
int
.(&a)[1]
est un pointeur d'int
unint
après le dernier élément du tableau. Ce pointeur estn
int
éléments après&a[0]
.(&a)[2]
est un pointeur d'int
unint
après le dernier élément de tableau de deux tableaux. Ce pointeur est2 * n
int
éléments après&a[0]
.(&a)[n]
est un pointeur d'int
unint
après le dernier élément de tableau den
tableaux. Ce pointeur est lesn * n
int
éléments après&a[0]
. Soustrayez simplement&a[0]
oua
et vous avezn
.Bien sûr, il s'agit d'un comportement techniquement indéfini même s'il fonctionne sur votre machine car
(&a)[n]
il ne pointe pas à l'intérieur du tableau ou au-delà du dernier élément du tableau (comme l'exigent les règles C d'arithmétique des pointeurs).la source
[n]
syntaxe déclare un tableau et les tableaux se décomposent en pointeurs. Trois choses utiles séparément avec cette conséquence.(&a)[n]
c'est le typeint[n]
, et cela s'exprime commeint*
dû à des tableaux exprimant comme l'adresse de leur premier élément, au cas où cela ne serait pas clair dans la description.Si vous avez deux pointeurs qui pointent vers deux éléments du même tableau, sa différence donnera le nombre d'éléments entre ces pointeurs. Par exemple, cet extrait de code affichera 2.
Considérons maintenant l'expression
Dans cette expression
a
a le typeint *
et pointe vers son premier élément.L'expression
&a
a un typeint ( * )[n]
et pointe vers la première ligne du tableau bidimensionnel imagé. Sa valeur correspond à la valeur dea
si les types sont différents.est le n-ième élément de ce tableau bidimensionnel imagé et a le type
int[n]
C'est-à-dire qu'il s'agit de la n-ième ligne du tableau imagé. Dans l'expression,(&a)[n] - a
il est converti en l'adresse de son premier élément et a le type `int *.Donc, entre
(&a)[n]
eta
il y a n rangées de n éléments. La différence sera donc égale àn * n
.la source
Donc,
(&a)[n]
estint[n]
a
estint
Maintenant, l'expression
(&a)[n]-a
effectue une soustraction de pointeur:la source