Warum sind die Adressen von argc und argv 12 Bytes voneinander entfernt?

Ich habe das folgende Programm auf meinem Computer (64-Bit-Intel unter Linux) ausgeführt.

#include <stdio.h>

void test(int argc, char **argv) {
    printf("[test] Argc Zeiger: %p\n", &argc);
    printf("[test] Argv Zeiger: %p\n", &argv);
}

int main(int argc, char **argv) {
    printf("Argc Zeiger: %p\n", &argc);
    printf("Argv Zeiger: %p\n", &argv);
    printf("Größe von &argc: %lu\n", sizeof (&argc));
    printf("Größe von &argv: %lu\n", sizeof (&argv));
    test(argc, argv);
    return 0;
}

Die Ausgabe des Programms war

$ gcc size.c -o size
$ ./size
Argc Zeiger: 0x7fffd7000e4c
Argv Zeiger: 0x7fffd7000e40
Größe von &argc: 8
Größe von &argv: 8
[test] Argc Zeiger: 0x7fffd7000e2c
[test] Argv Zeiger: 0x7fffd7000e20

Die Größe des Zeigers &argv beträgt 8 Bytes. Ich hatte erwartet, dass die Adresse von argc Adresse von (argv) + sizeof (argv) = 0x7ffed1a4c9f0 + 0x8 = 0x7ffed1a4c9f8 sein würde, aber es sind 4 Bytes dazwischen. Warum ist dies der Fall?

Meine Vermutung ist, dass es an der Speicherausrichtung liegen könnte, aber ich bin mir nicht sicher.

Das gleiche Verhalten stelle ich auch bei den Funktionen fest, die ich aufrufe.

Kommentare zu der Frage (6)
Lösung

Auf Ihrem System werden die ersten paar Ganzzahl- oder Zeigerargumente in Registern übergeben und haben keine Adressen. Wenn Sie deren Adressen mit &argc oder &argv übernehmen, muss der Compiler Adressen erzeugen, indem er die Registerinhalte in Stack-Speicherplätze schreibt und Ihnen die Adressen dieser Stack-Speicherplätze gibt. Dabei wählt der Compiler in gewissem Sinne die Stack-Speicherplätze, die ihm gerade in den Kram passen.

Kommentare (1)

Warum sind die Adressen von argc und argv 12 Bytes voneinander entfernt?

Aus der Sicht des Sprachstandards lautet die Antwort "kein besonderer Grund". C spezifiziert oder impliziert keine Beziehung zwischen den Adressen von Funktionsparametern. @EricPostpischil beschreibt, was wahrscheinlich in Ihrer speziellen Implementierung passiert, aber diese Details wären bei einer Implementierung anders, bei der alle Argumente auf dem Stack übergeben werden, und das ist nicht die einzige Alternative.

Außerdem fällt es mir schwer, einen Weg zu finden, wie solche Informationen in einem Programm nützlich sein könnten. Selbst wenn man zum Beispiel weiß, dass die Adresse von argv 12 Bytes vor der Adresse von argc liegt, gibt es immer noch keine definierte Möglichkeit, einen dieser Zeiger aus dem anderen zu berechnen.

Kommentare (19)