Ne peut pas comprendre cette façon de calculer le carré d’un nombre

J’ai trouvé une fonction qui calcule un carré d’un nombre:

int p(int n) { int a[n]; //works on C99 and above return (&a)[n] - a; } 

Il renvoie la valeur de n 2 . La question est, comment fait-il cela? Après un petit test, j’ai trouvé qu’entre (&a)[k] et (&a)[k+1] est la sizeof(a) / sizeof(int) . Pourquoi donc?

Evidemment, un hack … mais un moyen de numéroter un numéro sans utiliser l’opérateur * (il s’agissait d’une exigence de concours de codage).

 (&a)[n] 

est équivalent à un pointeur sur int à l’emplacement

 (a + sizeof(a[n])*n) 

et donc toute l’expression est

  (&a)[n] -a = (a + sizeof(a[n])*n -a) /sizeof(int) = sizeof(a[n])*n / sizeof(int) = sizeof(int) * n * n / sizeof(int) = n * n 

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 sur 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 de tableau) entre les pointeurs. Donc, si p pointe sur a[i] et q pointe sur a[j] , alors p - q est égal à i - j .

C11: 6.5.6 Opérateurs additifs (p9):

Lorsque deux pointeurs sont soustraits , les deux doivent pointer vers des éléments du même object tableau, ou vers le dernier élément de l’object tableau; le résultat est la différence des indices des deux éléments du tableau . […].
En d’autres termes, si les expressions P et Q désignent respectivement les iième et jième éléments d’un object tableau, l’expression (P)-(Q) a la valeur i−j condition que la valeur object de type ptrdiff_t .

Maintenant, je m’attends à ce que vous soyez au courant de la conversion du nom du tableau en pointeur, a convertit en pointeur vers le premier élément du tableau a . &a est l’adresse de l’ensemble du bloc mémoire, c’est-à-dire qu’il s’agit d’une adresse du tableau a . La figure ci-dessous vous aidera à comprendre ( lisez cette réponse pour une explication détaillée ):

entrer la description de l'image ici

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 d’ a ).

Donc, la déclaration

 return (&a)[n] - a; 

est équivalent à

 return (&a)[n] - (&a)[0]; 

et cette différence donnera le nombre d’éléments entre les pointeurs (&a)[n] et (&a)[0] , qui sont n tableaux chacun des n éléments int . Par conséquent, les éléments de tableau totaux sont n*n = n 2 .


REMARQUE:

C11: 6.5.6 Opérateurs additifs (p9):

Lorsque deux pointeurs sont soustraits, les deux doivent pointer vers des éléments du même object tableau, ou vers le dernier élément de l’object tableau ; le résultat est la différence entre les indices des deux éléments du tableau. La taille du résultat est définie par l’implémentation et son type (un type d’entier signé) est défini dans ptrdiff_t dans l’en-tête . Si le résultat n’est pas représentable dans un object de ce type, le comportement est indéfini.

Puisque (&a)[n] ne pointe ni vers des éléments du même object de tableau, ni vers un dernier élément du tableau, (&a)[n] - a invoquera un comportement non défini .

Notez également que mieux vaut changer le type de retour de la fonction p en ptrdiff_t .

a est un tableau (variable) de n int .

&a est un pointeur sur un tableau (variable) de n int .

(&a)[1] est un pointeur de int un int passé le dernier élément du tableau. Ce pointeur est n int éléments après &a[0] .

(&a)[2] est un pointeur de int un int passé le dernier élément de tableau de deux tableaux. Ce pointeur est 2 * n int éléments après &a[0] .

(&a)[n] est un pointeur de int un int passé le dernier élément de tableau de n tableaux. Ce pointeur est n * n int éléments après &a[0] . Il suffit de soustraire &a[0] ou a et vous avez n .

Bien sûr, il s’agit d’un comportement techniquement indéfini même si cela fonctionne sur votre machine car (&a)[n] ne pointe pas dans le tableau ni sur le dernier élément du tableau (comme requirejs par les règles C de l’arithmétique des pointeurs).

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.

 int a[10]; int *p1 = &a[1]; int *p2 = &a[3]; printf( "%d\n", p2 - p1 ); 

Maintenant, considérons l’expression

 (&a)[n] - a; 

Dans cette expression, a a type int * et pointe vers son premier élément.

Expression &a a le type int ( * )[n] et pointe vers la première ligne du tableau bidimensionnel imagé. Sa valeur correspond à la valeur d’ a si les types sont différents.

 ( &a )[n] 

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] et a il y a n lignes de n éléments. La différence sera donc égale à n * n .

 Expression | Value | Explanation a | a | point to array of int elements a[n] | a + n*sizeof(int) | refer to n-th element in array of int elements ------------------------------------------------------------------------------------------------- &a | a | point to array of (n int elements array) (&a)[n] | a + n*sizeof(int[n]) | refer to n-th element in array of (n int elements array) ------------------------------------------------------------------------------------------------- sizeof(int[n]) | n * sizeof(int) | int[n] is a type of n-int-element array 

Ainsi,

  1. type de (&a)[n] est int[n] pointeur
  2. type de pointeur est int

Maintenant, l’expression (&a)[n]-a effectue une soustraction de pointeur:

  (&a)[n]-a = ((a + n*sizeof(int[n])) - a) / sizeof(int) = (n * sizeof(int[n])) / sizeof(int) = (n * n * sizeof(int)) / sizeof(int) = n * n