Ich habe das folgende Programm auf meinem Computer ausgeführt (64-Bit-Intel unter 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
beträgt 8 Bytes. Ich habe erwartet, dass die Adresse von lautet argc
, address of (argv) + sizeof (argv) = 0x7ffed1a4c9f0 + 0x8 = 0x7ffed1a4c9f8
aber dazwischen befindet sich ein 4-Byte-Abstand. Warum ist das so?
Ich vermute, dass es an der Speicherausrichtung liegen könnte, bin mir aber nicht sicher.
Ich bemerke das gleiche Verhalten auch bei den Funktionen, die ich aufrufe.
c
memory-alignment
letmutx
quelle
quelle
main
.main
. In Cmain
kann als reguläre Funktion aufgerufen werden, daher muss es Argumente wie eine reguläre Funktion empfangen und dem ABI gehorchen.%zu
Antworten:
Auf Ihrem System werden die ersten paar Ganzzahl- oder Zeigerargumente in Registern übergeben und haben keine Adressen. Wenn Sie ihre Adressen mit
&argc
oder nehmen&argv
, muss der Compiler Adressen fabrizieren, indem er den Registerinhalt in Stapelpositionen schreibt und Ihnen die Adressen dieser Stapelpositionen gibt. Dabei wählt der Compiler gewissermaßen die Stapelpositionen aus, die für ihn geeignet sind.quelle
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 für eine Implementierung unterschiedlich, bei der alle Argumente auf dem Stapel übergeben werden, und dies ist nicht die einzige Alternative.
Außerdem habe ich Probleme, eine Möglichkeit zu finden, wie solche Informationen innerhalb eines Programms nützlich sein können. Selbst wenn Sie beispielsweise "wissen", dass die Adresse von
argv
12 Byte vor der Adresse von liegtargc
, gibt es immer noch keine definierte Möglichkeit, einen dieser Zeiger aus dem anderen zu berechnen.quelle
uintptr_t
Bezug auf die Durchführung der Konvertierung in und definiert sicherlich keine Beziehungen zwischen den Adressen von Parametern oder wo Argumente übergeben werden.uintptr_t
eine Adresse in den hohen 24 Bits und einige Authentifizierungsbits in den niedrigen 8 Bits ergeben. Wenn Sie dann 4 hinzufügen, wird die Authentifizierung nur noch fehlerhafter. es wird nicht aktualisiert…uintptr_t
Sie aus einer Conversion erhalten, ist nicht unbedingt eine einfache Adresse.(void *)(uintptr_t)(void *)p
die mit gleich vergleichbar ist(void *)p
. Es ist anzumerken, dass das Komitee fast genau dieses Thema kommentiert hat und zu dem Schluss gekommen ist, dass "Implementierungen ... Zeiger, die auf unterschiedlichen Ursprüngen basieren, auch als unterschiedlich behandeln können, obwohl sie bitweise identisch sind ".uintptr_t
Konvertierungen von Adressen und nicht als Unterschied zwischen Zeigern oder einer „bekannten“ Entfernung in Bytes berechnet wurde . Sicher, das stimmt, aber wie ist es nützlich? Es bleibt wahr, dass "es immer noch keine definierte Möglichkeit gibt, einen dieser Zeiger aus dem anderen zu berechnen", wie in der Antwort angegeben, aber diese Berechnung berechnet nichtb
ausa
, sondern berechnetb
aus beidena
undb
, dab
in der Subtraktion verwendet werden muss, um den Betrag zu berechnen hinzufügen. Das Berechnen voneinander ist nicht definiert.