Zur Frage des U-Boot

Finden Sie einen Grund für alles und Sie werden viel verstehen.


Als ich kürzlich den U-Boot-Code im Teil der SPI-Implementierung betrachtete, stieß ich auf ein Makro, um die Liste der verfügbaren Geräte zu umgehen, das mich nach mehreren Übergängen auf das Makro container_of zurücksetzte. Der Makrotext selbst war vorhanden, und mit ein wenig Erstaunen sah ich, dass er sich etwas von der Version unterschied, die ich zuvor gesehen hatte. Eine kleine Untersuchung wurde durchgeführt, die zu interessanten Ergebnissen führte.

Das Makro selbst ist seit langem bekannt und löst ein etwas seltsames Problem: Wir haben einen Zeiger auf ein Feld einer Struktur (ptr), wir kennen den Typ der Struktur (Typ) und den Namen des Feldes (Mitglied) und wir müssen einen Zeiger auf die Struktur als Ganzes erhalten. Ich verstehe nicht wirklich, wie eine solche Aufgabe aussehen könnte, vielleicht wollten die Autoren "etwas Seltsames", aber sobald die Aufgabe festgelegt ist, muss sie gelöst werden. Die Lösung ist bekannt:

#define container_of(ptr, type, member) \ ((type *)((char *)(ptr)-offsetof(type,member))) 

Alles ist transparent und klar, aber die entdeckte Implementierung war etwas komplizierter:

 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) 

Die zweite Zeile ist fast identisch mit der ersten Lösung, aber was macht die erste Zeile des Makros? Nein, was es tut, ist nur verständlich: Es erstellt einen konstanten lokalen Zeiger auf das Strukturfeld und weist ihm den Wert des Makroparameters zu - einen Zeiger auf das Feld. Aber warum dies getan wird, ist unklar.

Eine offensichtliche Überlegung bezüglich des Zwecks der ersten Zeile besteht darin, den ersten Parameter des Makros zu überprüfen, ob es sich tatsächlich um einen Zeiger auf ein Strukturfeld im Stil handelt:

  int *x = (X) 

was in diesem Fall eine etwas abstruse Form annimmt:

 typeof( ((type *)0)->member ) *__mptr = (ptr) 

da der für die Überprüfung erforderliche Typ im laufenden Betrieb erstellt werden muss.

Okay, stimmen wir zu, die Sache ist nützlich, aber ich musste den resultierenden Zeiger in der zweiten Zeile verwenden, um Compiler-Warnungen zu vermeiden.

Aber warum der Konstanzmodifikator - das Makro sollte auch ohne diesen Zusatz gut funktionieren (ich habe es sofort versucht und es hat funktioniert). Die Autoren konnten es nicht versehentlich sagen.

Es ist nicht notwendig, dort nachzuschauen
Ich muss zugeben, dass sie mich zur Antwort aufgefordert haben, ich selbst habe es nicht erraten.

Es stellt sich heraus, dass dieser Modifikator einfach notwendig ist, wenn wir versuchen, die Adresse der konstanten Struktur herauszufinden, da in diesem Fall der Compiler eine invalid conversion from 'const xxx*' to `xxx*' .

Es ist interessant, dass die Konstanz des Feldes selbst innerhalb einer gewöhnlichen Struktur nicht nur keinen Zwang in einem Makro erfordert, sondern auch die Notwendigkeit einer konstanten Struktur beseitigt, dh eines Ausdrucks wie:

 struct s1 { int data; const int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next); 

Kompiliert ohne Fehler und ohne Modifikator im Makrotext und den Ausdruck:

 struct s1 { int data; int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next); 

es ist erforderlich.

Für die Leser von Habr, die den Standard der Sprache gut kennen, werden meine Entdeckungen im Stil von „Danke, Kapitän“ lächerlich erscheinen, aber für den Rest mögen sie interessant sein.

Source: https://habr.com/ru/post/de466905/


All Articles