suma - raiz cuadrada de 4
No puedo entender esta forma de calcular el cuadrado de un número (5)
Obviamente, un truco ... pero una forma de cuadrar un número sin usar el operador
*
(este era un requisito del concurso de codificación).
(&a)[n]
es equivalente a un puntero a
int
en la ubicación
(a + sizeof(a[n])*n)
y así toda la expresión es
(&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
He encontrado una función que calcula el cuadrado de un número:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
Devuelve el valor de n
2
.
La pregunta es, ¿cómo hace eso?
Después de una pequeña prueba, descubrí que entre
(&a)[k]
y
(&a)[k+1]
es
sizeof(a)
/
sizeof(int)
.
¿Porqué es eso?
Para comprender este truco, primero debe comprender la diferencia del puntero, es decir, ¿qué sucede cuando se restan dos punteros que apuntan a elementos de la misma matriz ?
Cuando se sustrae un puntero de otro, el resultado es la distancia (medida en elementos de la matriz) entre los punteros.
Entonces, si
p
apunta a
a[i]
q
apunta a
a[j]
, entonces
p - q
es igual a
i - j
.
C11: 6.5.6 Operadores aditivos (p9):
Cuando se restan dos punteros , ambos deben apuntar a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz; El resultado es la diferencia de los subíndices de los dos elementos de la matriz . [...]
En otras palabras, si las expresionesP
yQ
apuntan, respectivamente, a los elementosi
-th yj
-th de un objeto de matriz, la expresión(P)-(Q)
tiene el valori−j
siempre que el valor se ajuste a un objeto de tipoptrdiff_t
.
Ahora espero que conozca la conversión del nombre de la matriz a puntero,
a
convierte en puntero al primer elemento de la matriz
a
.
&a
es la dirección de todo el bloque de memoria, es decir, es una dirección de la matriz
a
.
La siguiente figura lo ayudará a comprender (
lea
esta respuesta
para obtener una explicación detallada
):
Esto le ayudará a comprender por qué
&a
tiene la misma dirección y cómo
(&a)[i]
es la dirección de
la
matriz i (de igual tamaño que la de
a
).
Entonces, la declaración
return (&a)[n] - a;
es equivalente a
return (&a)[n] - (&a)[0];
y esta diferencia dará el número de elementos entre los punteros
(&a)[n]
y
(&a)[0]
, que son
n
matrices de cada uno de
n
elementos
int
.
Por lo tanto, los elementos de la matriz total son
n*n
=
n
2
.
NOTA:
C11: 6.5.6 Operadores aditivos (p9):
Cuando se restan dos punteros, ambos deben apuntar a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz ; El resultado es la diferencia de los subíndices de los dos elementos de la matriz. El tamaño del resultado está definido por la implementación , y su tipo (un tipo entero con signo) es
ptrdiff_t
definido en el encabezado<stddef.h>
. Si el resultado no es representable en un objeto de ese tipo, el comportamiento es indefinido.
Como
(&a)[n]
no apunta a elementos del mismo objeto de matriz ni a uno pasado el último elemento del objeto de matriz,
(&a)[n] - a
invocará
un comportamiento indefinido
.
También tenga en cuenta que, mejor cambiar el tipo de retorno de la función
p
a
ptrdiff_t
.
Si tiene dos punteros que apuntan a dos elementos de la misma matriz, entonces su diferencia dará el número de elementos entre estos punteros. Por ejemplo, este fragmento de código generará 2.
int a[10];
int *p1 = &a[1];
int *p2 = &a[3];
printf( "%d/n", p2 - p1 );
Ahora consideremos la expresión
(&a)[n] - a;
En esta expresión,
a
tipo tiene
int *
y apunta a su primer elemento.
La expresión
&a
tiene el tipo
int ( * )[n]
y apunta a la primera fila de la matriz bidimensional con imagen.
Su valor coincide con el valor de los tipos aunque son diferentes.
( &a )[n]
es el enésimo elemento de esta matriz bidimensional con imagen y tiene el tipo
int[n]
Es decir, es la enésima fila de la matriz con imagen.
En la expresión
(&a)[n] - a
se convierte a la dirección de su primer elemento y tiene el tipo `int *.
Entonces, entre
(&a)[n]
y
a
hay n filas de n elementos.
Entonces la diferencia será igual a
n * n
.
a
es una matriz (variable) de
n
int
.
&a
es un puntero a una matriz (variable) de
n
int
.
(&a)[1]
es un puntero de
int
one
int
pasado el último elemento de la matriz.
Este puntero es
n
int
elementos después de
&a[0]
.
(&a)[2]
es un puntero de
int
one
int
pasado el último elemento de matriz de dos matrices.
Este puntero tiene
2 * n
elementos
int
después de
&a[0]
.
(&a)[n]
es un puntero de
int
one
int
pasado el último elemento de matriz de
n
matrices.
Este puntero es
n * n
int
elementos después de
&a[0]
.
Simplemente resta
&a[0]
o
a
y tienes
n
.
Por supuesto, este es un comportamiento técnicamente indefinido, incluso si funciona en su máquina, ya que
(&a)[n]
no apunta dentro de la matriz o más allá del último elemento de la matriz (como lo requieren las reglas C de la aritmética del puntero).
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
Así,
-
tipo de
(&a)[n]
esint[n]
puntero -
tipo de
a
punteroint
Ahora la expresión
(&a)[n]-a
realiza una sustracción de puntero:
(&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