Im folgenden Codebit unterscheiden sich Zeigerwerte und Zeigeradressen wie erwartet.
Array-Werte und Adressen jedoch nicht!
Wie kann das sein?
Ausgabe
my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>
int main()
{
char my_array[100] = "some cool string";
printf("my_array = %p\n", my_array);
printf("&my_array = %p\n", &my_array);
char *pointer_to_array = my_array;
printf("pointer_to_array = %p\n", pointer_to_array);
printf("&pointer_to_array = %p\n", &pointer_to_array);
printf("Press ENTER to continue...\n");
getchar();
return 0;
}
sizeof(&array)
zurück?Antworten:
Der Name eines Arrays wertet üblicherweise an die Adresse des ersten Elements des Arrays, so
array
und&array
hat den gleichen Wert (aber verschiedene Typen, soarray+1
und&array+1
wird nicht gleich sein , wenn die Anordnung mehr als 1 Element ist lang).Hiervon gibt es zwei Ausnahmen: Wenn der Array-Name ein Operand von
sizeof
oder unär&
(Adresse von) ist, bezieht sich der Name auf das Array-Objekt selbst. Auf diese Weise erhaltensizeof array
Sie die Größe des gesamten Arrays in Bytes und nicht die Größe eines Zeigers.Für ein Array, das als definiert ist
T array[size]
, hat es den TypT *
. Wenn Sie es erhöhen, gelangen Sie zum nächsten Element im Array.&array
Wird an derselben Adresse ausgewertet, aber mit derselben Definition wird ein Zeiger vom Typ erstellt,T(*)[size]
dh es ist ein Zeiger auf ein Array und nicht auf ein einzelnes Element. Wenn Sie diesen Zeiger erhöhen, wird die Größe des gesamten Arrays hinzugefügt, nicht die Größe eines einzelnen Elements. Zum Beispiel mit Code wie diesem:Wir können erwarten, dass der zweite Zeiger 16 größer als der erste ist (da es sich um ein Array mit 16 Zeichen handelt). Da% p Zeiger normalerweise hexadezimal konvertiert, sieht es möglicherweise so aus:
quelle
&array
ist ein Zeiger auf das erste Element des Arrays, wobei sich asarray
auf das gesamte Array bezieht. Der grundlegende Unterschied kann auch durch einen Vergleich beobachtet werdensizeof(array)
, zusizeof(&array)
. Beachten Sie jedoch, dass nur, wenn Siearray
als Argument an eine Funktion übergeben,&array
tatsächlich nur übergeben wird. Sie können ein Array nur dann als Wert übergeben, wenn es mit a gekapselt iststruct
.&array[0]
wird, und nicht&array
in einen Zeiger auf das Array. Es mag ein Trottel sein, aber ich denke, es ist wichtig, klar zu machen; Compiler werden warnen, wenn die Funktion einen Prototyp hat, der dem Typ des übergebenen Zeigers entspricht.int *p = array; int **pp = &p;
.Dies liegt daran, dass sich der Array-Name (
my_array
) von einem Zeiger auf ein Array unterscheidet. Es ist ein Alias für die Adresse eines Arrays, und seine Adresse ist als die Adresse des Arrays selbst definiert.Der Zeiger ist jedoch eine normale C-Variable auf dem Stapel. Auf diese Weise können Sie die Adresse übernehmen und einen anderen Wert als die darin enthaltene Adresse abrufen.
Ich schrieb über dieses Thema hier - bitte werfen Sie einen Blick.
quelle
register
) unabhängig von ihrer Speicherdauer verwenden: statisch, dynamisch oder automatisch.my_array
selbst ist auf dem Stapel, weilmy_array
ist das gesamte Array.my_array
, wenn nicht das Subjekt der Operatoren&
odersizeof
, wird zu einem Zeiger auf sein erstes Element (dh&my_array[0]
) ausgewertet - abermy_array
selbst ist nicht dieser Zeiger (my_array
ist immer noch das Array). Dieser Zeiger ist nur ein kurzlebiger Wert (z. B. gegebenint a;
, es ist genau soa + 1
) - zumindest konzeptionell wird er "nach Bedarf berechnet". Der wahre "Wert" vonmy_array
ist der Inhalt des gesamten Arrays - es ist nur so, dass das Festhalten dieses Werts in C dem Versuch gleicht, Nebel in einem Glas aufzufangen.Wenn Sie in C den Namen eines Arrays in einem Ausdruck verwenden (einschließlich der Übergabe an eine Funktion), zerfällt es in einen Zeiger auf sein erstes Element , es sei denn, es ist der Operand des
&
Operators address-of ( ) oder dessizeof
Operators .Das heißt, in den meisten Kontexten
array
entspricht dies&array[0]
sowohl dem Typ als auch dem Wert.In Ihrem Beispiel
my_array
hat Typ,char[100]
der zu a zerfällt,char*
wenn Sie ihn an printf übergeben.&my_array
hat Typchar (*)[100]
(Zeiger auf Array von 100char
). Da es sich um den Operanden handelt,&
ist dies einer der Fälle,my_array
in denen ein Zeiger auf sein erstes Element nicht sofort verfällt.Der Zeiger auf das Array hat den gleichen Adresswert wie ein Zeiger auf das erste Element des Arrays, da ein Array-Objekt nur eine zusammenhängende Folge seiner Elemente ist, aber ein Zeiger auf ein Array hat einen anderen Typ als ein Zeiger auf ein Element von dieses Array. Dies ist wichtig, wenn Sie Zeigerarithmetik für die beiden Zeigertypen ausführen.
pointer_to_array
hat typechar *
- initialisiert, um auf das erste Element des Arrays zu zeigen, da diesmy_array
im Initialisierungsausdruck zerfällt - und&pointer_to_array
hat typechar **
(Zeiger auf einen Zeiger auf achar
).Von diesen:
my_array
(nach dem Zerfall aufchar*
)&my_array
undpointer_to_array
alle zeigen direkt auf das Array oder das erste Element des Arrays und haben daher den gleichen Adresswert.quelle
Der Grund, warum
my_array
und das&my_array
Ergebnis zu derselben Adresse führt, kann leicht verstanden werden, wenn Sie sich das Speicherlayout eines Arrays ansehen.Angenommen, Sie haben ein Array mit 10 Zeichen (stattdessen die 100 in Ihrem Code).
Speicher für
my_array
sieht ungefähr so aus:In C / C ++ zerfällt ein Array in den Zeiger auf das erste Element in einem Ausdruck wie z
Wenn Sie untersuchen, wo das erste Element des Arrays liegt, werden Sie feststellen, dass seine Adresse mit der Adresse des Arrays übereinstimmt:
quelle
In der Programmiersprache B, die der unmittelbare Vorgänger von C war, waren Zeiger und ganze Zahlen frei austauschbar. Das System würde sich so verhalten, als wäre der gesamte Speicher ein riesiges Array. Jedem Variablennamen war entweder eine globale oder eine stapelrelative Adresse zugeordnet. Für jeden Variablennamen musste der Compiler lediglich nachverfolgen, ob es sich um eine globale oder lokale Variable handelte, und seine Adresse relativ zur ersten globalen oder lokalen Variable.
Bei einer globalen Deklaration wie
i;
[es war nicht erforderlich, einen Typ anzugeben, da alles eine Ganzzahl / ein Zeiger war] würde der Compiler als:address_of_i = next_global++; memory[address_of_i] = 0;
und eine Anweisung wiei++
als: verarbeitetmemory[address_of_i] = memory[address_of_i]+1;
.Eine Erklärung wie
arr[10];
würde als verarbeitet werdenaddress_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
. Beachten Sie, dass der Compiler sofort vergessen kannarr
, ein Array zu sein , sobald diese Deklaration verarbeitet wurde . Eine Anweisung wiearr[i]=6;
würde als verarbeitet werdenmemory[memory[address_of_a] + memory[address_of_i]] = 6;
. Dem Compiler ist es egal, obarr
er ein Array undi
eine Ganzzahl darstellt oder umgekehrt. In der Tat wäre es egal, ob sie beide Arrays oder beide Ganzzahlen wären; Es würde den Code wie beschrieben sehr gerne generieren, ohne Rücksicht darauf, ob das resultierende Verhalten wahrscheinlich nützlich wäre.Eines der Ziele der Programmiersprache C war es, weitgehend mit B kompatibel zu sein. In B identifizierte der Name eines Arrays [in der Terminologie von B als "Vektor" bezeichnet] eine Variable, die einen Zeiger enthielt, auf den ursprünglich gezeigt wurde auf das erste Element einer Zuordnung der angegebenen Größe. Wenn dieser Name also in der Argumentliste für eine Funktion erscheint, erhält die Funktion einen Zeiger auf den Vektor. Obwohl C "echte" Array-Typen hinzufügte, deren Name fest mit der Adresse der Zuordnung verknüpft war und nicht mit einer Zeigervariablen, die anfänglich auf die Zuordnung verweisen würde, zerlegen sich Arrays in Zeiger, deren Code, der ein Array vom Typ C deklariert, sich identisch verhält zu B-Code, der einen Vektor deklariert und dann die Variable, die seine Adresse enthält, nie geändert hat.
quelle
Eigentlich
&myarray
undmyarray
beide sind die Basisadresse.Wenn Sie den Unterschied sehen möchten, anstatt zu verwenden
verwenden
quelle