Ich weiß, dass es theoretisch eine andere Indirektionsebene gibt, wenn auf sie zugegriffen wird,
Nun, es kommt darauf an, dass ein wirklich dummer Compiler zusätzliche Indirektionsebenen für eine Struktur einführt, aber ein intelligenter Compiler kann tatsächlich besseren Code erzeugen, wenn eine Struktur beteiligt ist.
Im Gegensatz zu anderen Antworten kennt der Compiler die Adresse globaler Variablen NICHT. Für globale Variablen in derselben Kompilierungseinheit ist ihre Position relativ zueinander bekannt, nicht jedoch ihre absolute Position. Für Variablen in anderen Kompilierungseinheiten weiß sie weder absolut noch relativ etwas über ihre Position.
Stattdessen enthält der vom Compiler generierte Code Platzhalter für die Positionen der globalen Variablen. Diese werden dann durch tatsächliche Positionen ersetzt, wenn die endgültige Adresse festgelegt wird (traditionell, wenn das Programm verknüpft ist, manchmal jedoch erst, wenn es ausgeführt wird ).
Obwohl es einige CPUs (wie die 6502) gibt, die ohne vorherige Einrichtung direkt auf einen globalen Speicherort zugreifen können, gibt es viele, die dies nicht können. Um beispielsweise auf arm auf eine globale Variable zuzugreifen, muss der Compiler normalerweise zuerst die Adresse der Variablen in ein Register laden, entweder unter Verwendung eines Literalpools oder in neueren Versionen von arm unter Verwendung von movw / movt. Greifen Sie dann mit einem registerrelativen Ladebefehl auf die globale Variable zu.
Dies bedeutet, dass für Variablen außerhalb der Kompilierungseinheit der Zugriff auf mehrere Elemente derselben globalen Struktur wahrscheinlich effizienter ist als der Zugriff auf einzelne globale Variablen.
Um dies zu testen, habe ich den folgenden Code mit ARM gcc 8.2 und -O3 Optimierung in godbolt eingegeben.
extern int a;
extern int b;
struct Foo {
int a;
int b;
};
extern struct Foo foo;
void f1 (void) {
a = 1;
b = 2;
}}
void f2 (void) {
foo.a = 1;
foo.b = 2;
}}
Dies führte zu
f1:
mov r0, # 1
mov r2, # 2
ldr r1, .L3
ldr r3, .L3 + 4
str r0, [r1]
str r2, [r3]
bx lr
.L3:
.word a
.word b
f2:
mov r1, # 1
mov r2, # 2
ldr r3, .L6
stm r3, {r1, r2}
bx lr
.L6:
.word foo
Im Fall von f1 sehen wir, dass der Compiler die Adressen von a und b getrennt von Literalpools lädt (die Adressen in den Literalpools werden später vom Linker ausgefüllt).
Im Fall von f2 muss der Compiler die Adresse der Struktur jedoch nur einmal aus dem Literalpool laden, noch besser, weil er weiß, dass die beiden Variablen im Speicher nebeneinander liegen, und sie mit einem einzigen "Speicher schreiben kannmehrere "Anweisungen anstelle von zwei separaten Speicheranweisungen.
aber von einem Stil-POV ist dies besser oder schlechter als das Einfügen in die Dateien globals.c und / oder globals.h?
IMO, das davon abhängt, ob die Variablen tatsächlich zusammenhängen oder nicht.