Warum liegen die Adressen von argc und argv 12 Bytes auseinander?

Lesezeit: 5 Minuten

Benutzeravatar von letmutx
letmutx

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

#include <stdio.h>

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

int main(int argc, char **argv) {
    printf("Argc Pointer: %p\n", &argc);
    printf("Argv Pointer: %p\n", &argv);
    printf("Size of &argc: %lu\n", sizeof (&argc));
    printf("Size of &argv: %lu\n", sizeof (&argv));
    test(argc, argv);
    return 0;
}

Die Ausgabe des Programms war

$ gcc size.c -o size
$ ./size
Argc Pointer: 0x7fffd7000e4c
Argv Pointer: 0x7fffd7000e40
Size of &argc: 8
Size of &argv: 8
[test] Argc Pointer: 0x7fffd7000e2c
[test] Argv Pointer: 0x7fffd7000e20

Die Größe des Zeigers &argv ist 8 Byte. Ich erwartete die Adresse von argc sein address of (argv) + sizeof (argv) = 0x7ffed1a4c9f0 + 0x8 = 0x7ffed1a4c9f8 Dazwischen befindet sich jedoch ein 4-Byte-Padding. Warum ist das so?

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

Ich bemerke das gleiche Verhalten bei den Funktionen, die ich auch aufrufe.

  • Warum nicht? Sie könnten 174 Bytes voneinander entfernt sein. Eine Antwort hängt von Ihrem Betriebssystem und/oder einer Wrapper-Bibliothek ab, die eingerichtet wird main.

    – Aschepler

    8. Februar 2020 um 15:38 Uhr

  • @aschepler: Es sollte nicht von einem Wrapper abhängen, der eingerichtet wird main. In C, main kann als reguläre Funktion aufgerufen werden, muss also wie eine reguläre Funktion Argumente empfangen und der ABI gehorchen.

    – Eric Postpischil

    8. Februar 2020 um 15:52 Uhr

  • @aschelper: Ich bemerke das gleiche Verhalten auch für andere Funktionen.

    – letmutx

    8. Februar 2020 um 15:55 Uhr


  • Es ist ein interessantes „Gedankenexperiment“, aber eigentlich gibt es nichts, was mehr sein sollte als ein „Ich frage mich, warum“. Diese Adressen können sich je nach Betriebssystem, Compiler, Compilerversion, Prozessorarchitektur ändern und sollten in keiner Weise im „echten Leben“ verwendet werden.

    – Neil

    9. Februar 2020 um 12:31 Uhr


  • das Ergebnis von sizeof muss mit gedruckt werden %zu

    – phuklv

    10. Februar 2020 um 3:22 Uhr

Auf Ihrem System werden die ersten Ganzzahl- oder Zeigerargumente in Registern übergeben und haben keine Adressen. Wenn Sie ihre Adressen mitnehmen &argc oder &argv, muss der Compiler Adressen fabrizieren, indem er die Registerinhalte in Stapelspeicherplätze schreibt und Ihnen die Adressen dieser Stapelspeicherplätze gibt. Dabei wählt der Compiler gewissermaßen die Stack-Positionen aus, die ihm gerade am besten passen.

  • Beachten Sie, dass dies passieren kann auch wenn sie auf dem Stack weitergegeben werden; der Compiler ist nicht verpflichtet, den Slot für eingehende Werte auf dem Stack als Speicher für die lokalen Objekte zu verwenden, in die die Werte gehen. Dies kann sinnvoll sein, wenn die Funktion schließlich zum Tail-Call führt und die aktuellen Werte dieser Objekte benötigt, um die ausgehenden Argumente für den Tail-Call zu erzeugen.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    8. Februar 2020 um 16:39 Uhr


Warum liegen die Adressen von argc und argv 12 Bytes auseinander?

Aus 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 an den Stapel ü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. Zum Beispiel, auch wenn Sie “wissen”, dass die Adresse von argv ist 12 Bytes vor der Adresse von argcgibt es immer noch keine definierte Möglichkeit, einen dieser Zeiger aus dem anderen zu berechnen.

  • @R..GitHubSTOPHELPINGICE: Die Berechnung eines aus dem anderen ist teilweise definiert, nicht gut definiert. Der C-Standard ist nicht streng, wie die Konvertierung erfolgen soll uintptr_t ausgeführt wird, und es definiert sicherlich keine Beziehungen zwischen den Adressen von Parametern oder wo Argumente übergeben werden.

    – Eric Postpischil

    8. Februar 2020 um 17:04 Uhr


  • @R..GitHubSTOPHELPINGICE: Die Tatsache, dass Sie einen Roundtrip durchführen können, bedeutet, dass g(f(x)) = x ist, wobei x ein Zeiger ist, f ist convert-pointer-to-uintptr_t und g ist convert-uintptr_t-to -Zeiger. Mathematisch und logisch bedeutet dies nicht, dass g(f(x)+4) = x+4. Wenn zum Beispiel f(x) x² und g(y) sqrt(y) wäre, dann wäre g(f(x)) = x (für reelles nicht negatives x), aber g(f(x)+4) ≠ x+4, im Allgemeinen. Bei Zeigern erfolgt die Umwandlung in uintptr_t könnte eine Adresse in den hohen 24 Bits und einige Authentifizierungsbits in den niedrigen 8 Bits angeben. Das Hinzufügen von 4 vermasselt dann nur die Authentifizierung; es wird nicht aktualisiert…

    – Eric Postpischil

    9. Februar 2020 um 0:43 Uhr

  • … die Adressbits. Oder die Konvertierung in uintptr_t könnte eine Basisadresse in den hohen 16 Bits und einen Offset in den niedrigen 16 Bits ergeben, und das Hinzufügen von 4 zu den niedrigen Bits könnte zu den hohen Bits führen, aber die Skalierung ist falsch (weil die dargestellte Adresse es nicht ist base•65536+offset, sondern ist base•64+offset, wie es in einigen Systemen der Fall war). Ganz einfach, die uintptr_t man bekommt bei einer umrechnung nicht unbedingt eine einfache adresse.

    – Eric Postpischil

    9. Februar 2020 um 0:45 Uhr

  • @R..GitHubSTOPHELPINGICE Aus meiner Lektüre des Standards gibt es nur eine schwache Garantie dafür (void *)(uintptr_t)(void *)p wird gleich vergleichen (void *)p. Und es ist erwähnenswert, dass das Komitee fast genau zu diesem Thema Stellung genommen hat und zu dem Schluss kam, dass „Implementierungen … auch Hinweise, die auf unterschiedlichen Ursprüngen beruhen, als unterschiedlich behandeln können obwohl sie bitweise identisch sind.”

    – Ryan Avella

    9. Februar 2020 um 8:21 Uhr


  • @R..GitHubSTOPHELPINGICE: Tut mir leid, ich habe übersehen, dass Sie einen Wert hinzugefügt haben, der als Differenz von zwei berechnet wurde uintptr_t Konvertierungen von Adressen anstelle von unterschiedlichen Zeigern oder einer „bekannten“ Entfernung in Bytes. Sicher, das stimmt, aber wie ist es nützlich? Es bleibt wahr, dass „es immer noch keinen definierten Weg gibt, einen dieser Zeiger aus dem anderen zu berechnen“, wie es in der Antwort heißt, aber diese Berechnung funktioniert nicht b aus a sondern rechnet b von beiden a und bseit b muss bei der Subtraktion verwendet werden, um den zu addierenden Betrag zu berechnen. Die Berechnung eines aus dem anderen ist nicht definiert.

    – Eric Postpischil

    9. Februar 2020 um 12:12 Uhr


1397190cookie-checkWarum liegen die Adressen von argc und argv 12 Bytes auseinander?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy