Eine Zeigervariable enthält eine Speicheradresse. Was Sie beachten müssen, ist, wie die C-Programmiersprache verwendet wird, um Quellcode in einer höheren Sprache zu schreiben, die dann für Sie in den Maschinencode konvertiert wird, der tatsächlich vom Computer verwendet wird.
Die C-Programmiersprache ist eine Sprache, die entworfen wurde, um die Verwendung von Computerhardware einfacher zu machen als die Verwendung von Baugruppencode oder Maschinencode. So verfügt es über Sprachfeatures, die das Schreiben von Quellcode erleichtern, der besser lesbar und einfacher zu verstehen ist als Assemblercode.
Wenn wir eine Zeigervariable in C als einen Zeiger auf einen Typ deklarieren, was wir sagen, ist der Compiler der Typ der Daten an dem Speicherort, dessen Adresse im Zeiger gespeichert ist. Allerdings weiß der Compiler nicht wirklich, ob wir ihm die Wahrheit sagen oder nicht. Die wichtigste Sache zu erinnern ist, dass eine tatsächliche Speicheradresse keinen Typ hat, es ist nur eine Adresse. Irgendwelche Typinformationen gehen verloren, wenn der Compiler den Quellcode in Maschinencode kompiliert.
Eine struct
ist eine Art Schablone oder Muster oder Schablone, die verwendet wird, um einen Speicherbereich virtuell zu überlagern, um zu bestimmen, wie die Bytes in dem Speicherbereich interpretiert werden sollen. Ein Programmierer kann bei der Arbeit mit Daten höhere Sprachfunktionen verwenden, ohne über Speicheradressen und Offsets Bescheid wissen zu müssen.
Wenn eine Variable als Strukturtyp definiert ist, dann wird ein Speicherbereich zugewiesen, der groß genug ist, um die Struktur zu speichern, und der Compiler ermittelt für Sie Memberoffsets. Wenn eine Variable als ein Zeiger auf einen Speicherbereich definiert ist, der wieder die Daten für diesen Typ enthalten soll, wird der Compiler die Offsets der Elemente für Sie herausfinden. Es liegt jedoch an Ihnen, die Zeigervariable mit der korrekten Adresse zu haben.
Also, wenn Sie eine Struktur in etwa wie folgt:
struct _tagStruct {
short sOne;
short sTwo;
};
Und Sie dann verwenden, wie zum Beispiel:
struct _tagStruct one; // allocate a memory area large enough for a struct
struct _tagStruct two; // allocate a memory area large enough for a struct
struct _tagStruct *three; // a pointer to a memory area to be interpreted as a struct
one.sOne = 5; // assign a value to this memory area interpreted as a short
one.sTwo = 7; // assign a value to this memory area interpreted as a
two = one; // make a copy of the one memory area in another
three = &one; // assign an address of a memory area to our pointer
three->sOne = 405; // modify the memory area pointed to, one.sOne in this case
Sie brauchen nicht über die Details des Speicherlayout zu kümmern der Struktur und Offsets zu den Strukturelementen. Und die Zuordnung einer Struktur zu einer anderen ist lediglich eine Zuweisungsanweisung. Das alles funktioniert also eher auf menschlicher Ebene als auf maschineller Ebene.
Was aber, wenn ich eine Funktion habe, short funcOne (short *inoutOne)
, die ich mit dem sOne
Mitglied der Struktur one
verwenden möchte? Ich kann das funcOne(&one.sOne)
tun, das die Funktion funcOne()
mit der Adresse des sOne
Mitgliedes der struct _tagStruct
Variablen one
aufruft.
Eine typische Implementierung dieses in Maschinencode wird das Element der Adresse der Variablen one
in ein Register, fügt den Offset zu laden sOne
und rief dann die Funktion funcOne()
mit dieser berechneten Adresse.
Ich könnte auch etwas ähnliches mit einem Zeiger, funcOne(&three->sOne)
.
Eine typische Implementierung dieses in Maschinencode wird das Mitglied Des Inhalt der Zeigervariable three
in ein Register, fügt den Offset zu laden sOne
und rief dann die Funktion funcOne()
mit dieser berechneten Adresse.
also in einem Fall laden wir die Adresse einer Variablen in ein Register, bevor der Versatz hinzufügen und im zweiten Fall laden wir den Inhalt einer Variablen in ein Register, bevor der Offset hinzugefügt wird. In beiden Fällen verwendet der Compiler einen Offset, bei dem es sich normalerweise um die Anzahl der Bytes vom Anfang der Struktur zum Member der Struktur handelt. Im Falle des ersten Mitglieds sOne
von struct _tagStruct
wäre dieser Offset null Bytes, da es das erste Mitglied der Struktur ist. Für viele Compiler wäre der Offset des zweiten Elements sTwo
zwei Bytes, da die Größe eines short
zwei Bytes ist.
Allerdings kann der Compiler Entscheidungen über das Layout einer Struktur treffen, sofern nicht ausdrücklich anders angegeben, so dass auf einigen Computern der Offset von Element sTwo
vier Bytes betragen kann, um effizienteren Maschinencode zu erzeugen.
Die Verwendung der C-Programmiersprache ermöglicht uns ein gewisses Maß an Unabhängigkeit von der zugrunde liegenden Computerhardware, es sei denn, es gibt einen Grund für uns, diese Details zu behandeln. Der C-Sprachen-Standard gibt Operatoren-Präzedenzbedeutung an, wenn verschiedene Operatoren in einer Anweisung zusammengemischt werden und Klammern nicht verwendet werden, um eine exakte Reihenfolge der Auswertung für den Ausdruck anzugeben, dann verwendet der Compiler diese Standardregeln, um zu bestimmen, wie die C Ausdruck in den richtigen Maschinencode (siehe Operator precedence table for the C programming language).
Sowohl der Operator Punkt (.) Als auch der Operator Dereferenz (->) haben die gleiche Priorität wie die höchste Priorität der Operatoren. Wenn Sie also einen Ausdruck wie &three->sOne
schreiben, dann verwandelt der Compiler ihn in einen Ausdruck, der aussieht wie &(three->sOne)
. Dies verwendet die Adresse des Operators, um eine Adresse des sOne
Mitglieds des Speicherbereichs zu berechnen, auf den die Zeigervariable three
zeigt.
ein anderer Ausdruck würde (&three)->sOne
sein, die tatsächlich einen Compiler Fehler auslösen sollten, da &three
keinen Zeiger auf einen Speicherbereich ist ein struct _tagStruct
Werthalte sondern ein Zeiger auf einen Zeiger, da three
ist ein Zeiger auf eine Variable vom Typ struct _tagStruct
und keine Variable vom Typ struct _tagStruct
.
Ich verstehe, wie 'p-> Mitglied' und' (* p) .Member' funktionieren. Was ich nicht verstanden habe, ist '& p-> Mitglied'. –
@ PrashanthChandra, aktualisiert, um zu klären. – paxdiablo