Ich habe eine Funktion gefunden, die das Quadrat einer Zahl berechnet:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
Es gibt den Wert von n 2 zurück . Die Frage ist, wie macht das das? Nach ein wenig Test fand ich das zwischen (&a)[k]
und (&a)[k+1]
ist sizeof(a)
/ sizeof(int)
. Warum ist das so?
int p(n)
? Kompiliert das überhaupt?int q(int n) { return sizeof (char [n][n]); }
sizeof
, das Speichern von Zeichen. Alle anderen: Dies ist absichtlich dunkler Code, es ist undefiniertes Verhalten, @ ouahs Antwort ist richtig.Antworten:
Offensichtlich ein Hack ... aber eine Möglichkeit, eine Zahl ohne Verwendung des
*
Operators zu quadrieren (dies war eine Anforderung für einen Codierungswettbewerb).entspricht einem Zeiger auf
int
at locationund somit ist der gesamte Ausdruck
quelle
(&a)
als ein Zeiger auf ein Objekt ,n*sizeof(int)
wenn zumn
Zeitpunkt der Kompilierung nicht bekannt ist. C war früher eine einfache Sprache ...Um diesen Hack zu verstehen, müssen Sie zuerst den Zeigerunterschied verstehen, dh was passiert, wenn zwei Zeiger, die auf Elemente desselben Arrays zeigen, subtrahiert werden?
Wenn ein Zeiger von einem anderen subtrahiert wird, ist das Ergebnis der Abstand (gemessen in Array-Elementen) zwischen den Zeigern. Wenn also
p
aufa[i]
undq
zeigt aufa[j]
, dannp - q
ist gleichi - j
.C11: 6.5.6 Additive Operatoren (S. 9):
Jetzt erwarte ich, dass Sie wissen, dass der Array-Name in einen Zeiger
a
konvertiert und in einen Zeiger auf das erste Element des Arrays konvertiert wirda
.&a
ist die Adresse des gesamten Speicherblocks, dh es ist eine Adresse des Arraysa
. Die folgende Abbildung hilft Ihnen beim Verständnis ( lesen Sie diese Antwort für eine detaillierte Erklärung ):Dies hilft Ihnen zu verstehen, warum
a
und&a
hat die gleiche Adresse und wie(&a)[i]
ist die Adresse des i- ten Arrays (von der gleichen Größe wie die vona
).Also die Aussage
ist äquivalent zu
und dieser Unterschied gibt die Anzahl der Elemente zwischen den Zeigern
(&a)[n]
und an(&a)[0]
, dien
Arrays jedesn
int
Elements sind. Daher sind die gesamten Array-Elementen*n
=n
2 .HINWEIS:
C11: 6.5.6 Additive Operatoren (S. 9):
Da
(&a)[n]
weder auf Elemente desselben Array-Objekts noch auf Elemente hinter dem letzten Element des Array-Objekts verweist,(&a)[n] - a
wird undefiniertes Verhalten aufgerufen .Beachten Sie auch, dass es besser ist, den Rückgabetyp der Funktion
p
in zu ändernptrdiff_t
.quelle
&a[k]
ist eine Adresse desk
Elements des Arraysa
. Es ist(&a)[k]
das, was immer als Adresse eines Arrays vonk
Elementen betrachtet wird. So ist das erste Element an der Positiona
(oder&a
), die zweite an der Positiona
+ (Anzahl der Elemente des Arrays ,a
welche istn
) * (Größe eines Array - Elements) und so weiter. Beachten Sie, dass der Speicher für Arrays mit variabler Länge auf dem Stapel und nicht auf dem Heap zugewiesen wird.a
ist ein (variables) Array vonn
int
.&a
ist ein Zeiger auf ein (variables) Array vonn
int
.(&a)[1]
ist ein Zeiger vonint
einsint
nach dem letzten Array-Element. Dieser Zeiger istn
int
Elemente nach&a[0]
.(&a)[2]
ist ein Zeiger vonint
einsint
nach dem letzten Array-Element von zwei Arrays. Dieser Zeiger ist2 * n
int
Elemente nach&a[0]
.(&a)[n]
ist ein Zeiger vonint
einsint
nach dem letzten Array-Element vonn
Arrays. Dieser Zeiger istn * n
int
Elemente nach&a[0]
. Einfach subtrahieren&a[0]
odera
und du hastn
.Dies ist natürlich ein technisch undefiniertes Verhalten, selbst wenn es auf Ihrem Computer
(&a)[n]
funktioniert, da es nicht innerhalb des Arrays oder nach dem letzten Array-Element zeigt (wie in den C-Regeln der Zeigerarithmetik vorgeschrieben).quelle
[n]
Syntax deklariert ein Array und Arrays zerlegen sich in Zeiger. Drei getrennt nützliche Dinge mit dieser Konsequenz.(&a)[n]
ein Typint[n]
ist , der sichint*
aufgrund von Arrays ausdrückt, die als Adresse ihres ersten Elements ausgedrückt werden , falls dies in der Beschreibung nicht klar war.Wenn Sie zwei Zeiger haben, die auf zwei Elemente desselben Arrays zeigen, ergibt die Differenz die Anzahl der Elemente zwischen diesen Zeigern. Zum Beispiel gibt dieses Code-Snippet 2 aus.
Betrachten wir nun den Ausdruck
In diesem Ausdruck
a
hat Typint *
und zeigt auf sein erstes Element.Der Ausdruck
&a
hat Typint ( * )[n]
und zeigt auf die erste Zeile des abgebildeten zweidimensionalen Arrays. Sein Wert entspricht dem Wert,a
obwohl die Typen unterschiedlich sind.ist das n-te Element dieses abgebildeten zweidimensionalen Arrays und hat den Typ
int[n]
Das heißt, es ist die n-te Zeile des abgebildeten Arrays. Im Ausdruck(&a)[n] - a
wird es in die Adresse seines ersten Elements konvertiert und hat den Typ `int *.Also zwischen
(&a)[n]
unda
es gibt n Reihen von n Elementen. Der Unterschied wird also gleich seinn * n
.quelle
So,
(&a)[n]
istint[n]
Zeigera
istint
ZeigerJetzt führt der Ausdruck
(&a)[n]-a
eine Zeigersubtraktion durch:quelle