2016-07-25 21 views
6
eine Funktion Argumente Iterieren durch

I, ob der folgenden C-Code auf das C99 und/oder C11-Standard (s) möchte wissen, haftet:durch die Verwendung eines Zeigers auf den ersten

void foo(int bar0, int bar1, int bar2) { 
    int *bars = &bar0; 

    printf("0: %d\n1: %d\n2: %d\n", bars[0], bars[1], bars[2]); 
} 

int main(int argc, char **argv) { 
    foo(8, 32, 4); 

    return 0; 
} 

Dieses Codefragment Kompilierungen und läuft wie erwartet, wenn Visual Studio 2013 und Drucke mit:

0: 8
1: 32
2: 4

+1

Sie möchten Ihre Neugier befriedigen, oder haben Sie ein Problem, von dem Sie glauben, dass es das löst? – StoryTeller

+0

Nur aus Neugier, da variadic Argumente diese Technik verwenden, um über seine Argumente zu iterieren. –

Antwort

11

Nein, nicht irgendwo in der Nähe.

Der C-Standard garantiert nicht, dass die Funktionsargumente in aufeinanderfolgenden Speicherplätzen gespeichert werden (oder eine andere spezifische Reihenfolge,). Es ist Sache des Compilers und/oder der Plattform (Architektur) zu entscheiden, wie die Funktionsargumente an die Funktion übergeben werden.

Um noch mehr Klarheit zu geben, gibt es sogar keine Garantie, dass die Argumente, die zu übergeben sind, überhaupt im Speicher (z. B. Stapel) gespeichert werden. Sie können auch die Hardware-Register (, wenn anwendbar), für einige oder alle Parameter verwenden, um die Operationen schnell zu machen. Zum Beispiel

  • PowerPC

    Die PowerPC-Architektur verfügt über eine große Anzahl von Registern so dass die meisten Funktionen alle Argumente in den Registern für einzelne Ebene Anrufe passieren können. [...]

  • MIPS

    Die am häufigsten verwendete Aufrufkonvention für 32-Bit-MIPS ist das O32 ABI, die auf eine Funktion, die ersten vier Argumente gelangen in den Registern $a0 - $a3; nachfolgende Argumente werden auf dem Stapel übergeben. [...]

  • X86

    Die x86-Architektur ist mit vielen verschiedenen Aufrufkonventionen verwendet. Aufgrund der geringen Anzahl von Architekturregistern übergibt die x86-Aufrufkonvention hauptsächlich Argumente auf dem Stapel, während der Rückgabewert (oder ein Zeiger darauf) in einem Register übergeben wird.

und so weiter. Überprüfen Sie die full wiki article here.

also in Ihrem Fall ist bars[0] ein gültig Zugang, aber ob bars[1] und bars[2] gültig sind, hängt von der zugrunde liegenden Umgebung (Plattform/Compiler), vollständig. Am besten verlassen Sie sich nicht auf das Verhalten, das Sie erwarten.

Das heißt, nur nitpick, falls Sie die Argumente nicht benutzen (falls vorhanden) zu main() geben, können Sie einfach die Signatur int main(void) { reduzieren.

+0

+1, aber vielleicht sollte man hinzufügen, dass auf modernen Architekturen die ersten Funktionsparameter überhaupt nicht im Speicher gespeichert sind, sondern Hardware-Register durchlaufen haben. Und vielleicht entscheidet auch nicht der Compiler, sondern die Plattform-API. –

+0

@JensGustedt Sir, fügte ein bisschen mehr Infos auf dieser Front hinzu. Ist es jetzt besser? –

5

Kein Standard unterstützt dies. Es ist extrem unartig.

Array-Indizierung und Zeigerarithmetik ist nur für Arrays gültig. (Beachten Sie eine kleine Ausnahme. Sie können ein Zeiger eine Vergangenheit ein Array lesen oder einen Skalar, aber Sie können nicht Ehrerbietung it)

7

Nein, es entspricht keinem veröffentlichten Standard. Wie Argumente und lokale Variablen gespeichert werden und wo, hängt vom Compiler ab. Was in einem Compiler funktionieren könnte, funktioniert möglicherweise nicht in einem anderen oder sogar in einer anderen Version desselben Compilers.

Die C-Spezifikation erwähnt nicht einmal einen Stapel, alles, was es spezifiziert, sind die Scoping-Regeln.

+0

Variadic-Funktionen scheinen ebenfalls Teil des Standards zu sein - va_start, va_end sind Teil der Standardbibliothek, und genau das machen diese Makros - indem sie die Stack-Parameter durchgehen. – MichaelMoser

+3

@MichaelMoser Ja, aber wie diese Makros implementiert sind, ist nicht im Standard. Und einen Stack zu haben ist keine Voraussetzung für einen C-Compiler (insbesondere weil er nicht in der C-Spezifikation enthalten ist), es ist einfach ein praktisches Implementierungsdetail. –