2016-10-21 3 views
0

Ich lese this book, und ich fand diesen Code-Schnipsel in Kapitel 14.Zugriff auf struct Mitglied der Adresse des Zeigers in C

struct kobject *cdev_get(struct cdev *p) 
{ 
    struct module *owner = p->owner; 
    struct kobject *kobj; 
    if (owner && !try_module_get(owner)) 
     return NULL; 
    kobj = kobject_get(&p->kobj); 
    if (!kobj) 
     module_put(owner); 
    return kobj; 
} 

Ich verstehe, dass diese dereferenziert p, ein cdev Zeiger dann sein Besitzer Mitglied greift

p->owner // (*p).owner 

Wie funktioniert das? Es scheint, als ob es die Speicheradresse eines cdev-Zeigers dereferenziert und dann auf das kobj-Element des Zeigers selbst zugreift.

Ich dachte Zeiger waren nicht viel mehr als Speicheradressen, so dass ich nicht verstehe, wie sie Mitglieder haben können. Und wenn es versuchte, auf ein Mitglied des Zeigers selbst zuzugreifen, warum nicht einfach p.kobj?

Antwort

3

Wie pro p als struct cdev *p definiert ist, ist p sehr viel ein „Speicheradresse“ aber das ist nicht alle es ist - es auch ein Typ hat attached to it.

Da der Ausdruck *ptr ist „das Objekt durch ptr hingewiesen“, dass auch den Typen hat angebracht, so dass man logisch (*ptr).member tun können.

Und, da ptr->member mit (*ptr).member identisch ist, ist es auch gültig.

Fazit ist, Ihre Behauptung, dass "Zeiger [sind nicht] viel mehr als Speicheradressen" ist richtig. Aber sie sind ein wenig bisschen mehr :-)


In Bezug auf &ptr->member, scheinen Sie, dass als (&ptr)->member zu lesen, die nicht korrekt ist.

Stattdessen nach C Vorrangregeln ist es tatsächlich &(ptr->member), was die Adresse des Mitglieds dieser Struktur bedeutet.

Diese Vorrangregeln sind tatsächlich durch den ISO C-Standard spezifiziert (in diesem Fall C11). Von 6.5 Expressions, Fußnote 85:

Die Syntax gibt den Vorrang der Operatoren in der Auswertung eines Ausdrucks, die die gleiche wie die Reihenfolge der wichtigsten Subklauseln dieses Subklausel ersten, höchste Priorität ist.

Und da 6.5.2 Postfix operators (das Bit -> abdeckt) kommt, bevor 6.5.3 Unary operators (das Bit Abdeckung &), die eine erste Einrichtung -> auswertet.

+0

Ich verstehe, wie 'p-> Mitglied' und' (* p) .Member' funktionieren. Was ich nicht verstanden habe, ist '& p-> Mitglied'. –

+0

@ PrashanthChandra, aktualisiert, um zu klären. – paxdiablo

0

->member hat höhere Priorität als &.

&p->kobj 

parst als

&(p->kobj) 

das heißt es wird die Adresse des kobj Mitglied der struct p durch spitze nehmen.

0

Sie haben die Reihenfolge der Vorgänge falsch:

&p->kobj // &(p->kobj) 
1

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.

+0

Das war weit über das, was ich gefragt hatte und sehr informativ, danke. –

+1

@PrashanthChandra Ich bin froh, dass es hilfreich war. Manchmal fange ich einfach an etwas an und verbringe viel mehr Zeit als vernünftig. lol.Dies scheint ein Bereich zu sein, mit dem die Leute Probleme haben, also dachte ich mir, ich könnte etwas mehr Aufwand investieren und vielleicht jemandem helfen. –

Verwandte Themen