Znalazłem funkcję, która oblicza kwadrat liczby:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
Zwraca wartość n 2 . Pytanie brzmi, jak to się dzieje? Po krótkich testach stwierdziłem, że między (&a)[k]
a (&a)[k+1]
jest sizeof(a)
/ sizeof(int)
. Dlaczego?
int p(n)
? Czy to się w ogóle kompiluje?int q(int n) { return sizeof (char [n][n]); }
sizeof
było zapisywanie znaków. Wszyscy inni: to celowo niejasny kod, niezdefiniowane zachowanie, odpowiedź @ ouah jest poprawna.Odpowiedzi:
Oczywiście hack ... ale sposób na podniesienie liczby do kwadratu bez użycia
*
operatora (był to wymóg zawodów w kodowaniu).jest odpowiednikiem wskaźnika do
int
at locationa zatem całe wyrażenie jest
źródło
(&a)
jako wskaźnik do obiektu,n*sizeof(int)
kiedyn
nie jest znany w czasie kompilacji. C kiedyś był prostym językiem ...Aby zrozumieć ten hack, najpierw musisz zrozumieć różnicę wskaźników, tj. Co się stanie, gdy odejmie się dwa wskaźniki wskazujące na elementy tej samej tablicy ?
Kiedy jeden wskaźnik jest odejmowany od drugiego, wynikiem jest odległość (mierzona w elementach tablicy) między wskaźnikami. Tak więc, jeśli
p
wskazujea[i]
iq
wskazuje naa[j]
, top - q
jest równei - j
.C11: 6.5.6 Operatory addytywne (p9):
Teraz oczekuję, że jesteś świadomy konwersji nazwy tablicy na wskaźnik,
a
konwertuje na wskaźnik do pierwszego elementu tablicya
.&a
to adres całego bloku pamięci, czyli adres tablicya
. Poniższy rysunek pomoże ci zrozumieć ( przeczytaj tę odpowiedź, aby uzyskać szczegółowe wyjaśnienie ):Pomoże ci to zrozumieć, dlaczego
a
i&a
ma ten sam adres i jaki(&a)[i]
jest adres i- tej tablicy (tego samego rozmiaru co adresa
).A więc oświadczenie
jest równa
a ta różnica da liczbę elementów między wskaźnikami
(&a)[n]
i(&a)[0]
, które sąn
tablicami każdego zn
int
elementów. Dlatego łączna liczba elementów tablicy wynosin*n
=n
2 .UWAGA:
C11: 6.5.6 Operatory addytywne (p9):
Ponieważ
(&a)[n]
żaden z elementów tego samego obiektu tablicy ani jeden za ostatnim elementem obiektu tablicy nie(&a)[n] - a
wywoła niezdefiniowanego zachowania .Zauważ też, że lepiej zmienić zwracany typ funkcji
p
naptrdiff_t
.źródło
&a[k]
jest adresemk
th elementu tablicya
. To(&a)[k]
zawsze będzie uważany za adres tablicyk
elementów. Zatem pierwszy element jest na pozycjia
(lub&a
), drugi na pozycjia
+ (liczba elementów tablicy,a
która wynosin
) * (rozmiar elementu tablicy) i tak dalej. Zauważ, że pamięć dla tablic o zmiennej długości jest przydzielana na stosie, a nie na stercie.a
jest (zmienną) tablicąn
int
.&a
jest wskaźnikiem do (zmiennej) tablicyn
int
.(&a)[1]
jest wskaźnikiem oint
jedenint
za ostatnim elementem tablicy. Ten wskaźnik znajduje sięn
int
po elementach&a[0]
.(&a)[2]
jest wskaźnikiem oint
jedenint
za ostatnim elementem tablicy dwóch tablic. Ten wskaźnik znajduje się2 * n
int
po elementach&a[0]
.(&a)[n]
jest wskaźnikiem oint
jedenint
za ostatnim elementemn
tablicy. Ten wskaźnik znajduje sięn * n
int
po elementach&a[0]
. Po prostu odejmij&a[0]
luba
i maszn
.Oczywiście jest to technicznie niezdefiniowane zachowanie, nawet jeśli działa na twojej maszynie, ponieważ
(&a)[n]
nie wskazuje wewnątrz tablicy ani o jeden za ostatnim elementem tablicy (zgodnie z wymaganiami reguł C arytmetyki wskaźników).źródło
[n]
składnia deklaruje tablicę, a tablice rozkładają się na wskaźniki. Trzy osobno przydatne rzeczy z tą konsekwencją.(&a)[n]
jest to typint[n]
, a to wyraża się jakoint*
dzięki tablicom wyrażającym się jako adres ich pierwszego elementu, na wypadek, gdyby nie było to jasne w opisie.Jeśli masz dwa wskaźniki, które wskazują na dwa elementy tej samej tablicy, to ich różnica da liczbę elementów między tymi wskaźnikami. Na przykład ten fragment kodu wyświetli 2.
Rozważmy teraz wyrażenie
W tym wyrażeniu
a
ma typint *
i wskazuje na swój pierwszy element.Wyrażenie
&a
ma typint ( * )[n]
i wskazuje pierwszy wiersz obrazowanej dwuwymiarowej tablicy. Jego wartość jest zgodna z wartością,a
chociaż typy są różne.jest n-tym elementem tej obrazowanej dwuwymiarowej tablicy i ma typ
int[n]
To jest n-ty wiersz obrazowanej tablicy. W wyrażeniu(&a)[n] - a
jest konwertowany na adres swojego pierwszego elementu i ma typ `int *.Więc pomiędzy
(&a)[n]
ia
istnieje n rzędów n elementów. Więc różnica będzie równan * n
.źródło
A zatem,
(&a)[n]
toint[n]
wskaźnika
toint
wskaźnikTeraz wyrażenie
(&a)[n]-a
wykonuje odejmowanie wskaźnika:źródło