2010-03-15 10 views
6

In jeder Schleifeniteration wird Variable j immer wieder deklariert. Warum ist dann die Adresse gleich?Gleicher Speicherplatz wird wieder zugewiesen & wieder

  • Sollte es nicht jedes Mal eine zufällige Adresse gegeben werden?
  • Ist dieser Compiler abhängig?
#include<stdio.h> 
#include<malloc.h> 

int main() 
{ 
    int i=3; 
    while (i--) 
    { 
     int j; 
     printf("%p\n", &j); 
    } 
    return 0; 
} 

Testrun: -

[email protected]:~/c$ gcc test.c 
[email protected]:~/c$ ./a.out 
0x7fffc0b8e138 
0x7fffc0b8e138 
0x7fffc0b8e138 
[email protected]:~/c$ 
+0

Ich habe versehentlich die falsche Frage gepostet .. Meine schlechte .. Entschuldigung ..Ich habe die Frage –

+0

@Gardener aktualisiert: es wird gut kompilieren. 'void *' wird automatisch umgewandelt. –

+0

Sie haben die Frage jetzt geändert. Hast du es getestet, um zu sehen, ob deine ursprüngliche Behauptung immer noch wahr ist? Es ist nicht einmal ein gültiger Code, also ist die Antwort fast sicher nein. – Clifford

Antwort

6

Der Grund, warum die Adresse j ändert sich nie, weil der Compiler Speicher für j auf dem Stapel reserviert, wenn die Funktion im Gegensatz zu betreten, wenn j kommt in Umfang.

Wie immer kann es hilfreich sein, wenn Sie sich den Assembler-Code anschauen. Nehmen Sie die folgende Funktion: -

int foo(void) 
    { 
    int i=3; 
    i++; 
     { 
     int j=2; 
     i=j; 
     } 
    return i; 
    } 

gcc wandelt diese in der folgenden x86-Assembler-Code: -

foo: 
    pushl %ebp     ; save stack base pointer 
    movl %esp, %ebp   ; set base pointer to old top of stack 
    subl $8, %esp    ; allocate memory for local variables 
    movl $3, -4(%ebp)   ; initialize i 
    leal -4(%ebp), %eax  ; move address of i into eax 
    incl (%eax)    ; increment i by 1 
    movl $2, -8(%ebp)   ; initialize j 
    movl -8(%ebp), %eax  ; move j into accumulator 
    movl %eax, -4(%ebp)  ; set i to j 
    movl -4(%ebp), %eax  ; set the value of i as the function return value 
    leave      ; restore stack pointers 
    ret       ; return to caller 

Lasst uns dieses Assembler-Code zu Fuß durch. Die erste Zeile speichert den aktuellen Stapel-Basiszeiger, so dass er wiederhergestellt werden kann, wenn die Funktion beendet wird, die zweite Zeile legt den aktuellen Anfang des Stapels als neuen Stapel-Basiszeiger für diese Funktion fest.

Die dritte Zeile ist diejenige, die den Speicher auf dem Stapel für alle lokalen Variablen reserviert. Der Befehl subl $8, %esp subtrahiert 8 vom aktuellen Anfang des Stapelzeigers, dem esp Register. Stapel werden im Speicher größer, sodass diese Codezeile den Speicher im Stapel um 8 Byte erhöht. Wir haben zwei Ganzzahlen in dieser Funktion, i und j, von denen jede 4 Bytes benötigt, weshalb sie 8 Bytes zuweist.

Zeile 4 initialisiert i zu 3, indem direkt in eine Adresse auf dem Stapel geschrieben wird. Die Zeilen 5 und 6 laden und inkrementieren dann i. Zeile 7 initialisiert j, indem der Wert 2 in den Speicher geschrieben wird, der für j auf dem Stapel reserviert ist. Beachten Sie, dass, wenn in Zeile 7 in den Geltungsbereich kam, der Assembly-Code den Stack nicht angepasst hat, um Speicher für ihn zuzuweisen, der bereits früher erledigt wurde.

Ich bin mir sicher, dass es offensichtlich ist, aber der Grund, warum der Compiler den Speicher für alle lokalen Variablen zu Beginn der Funktion reserviert, ist, weil es viel effizienter ist, dies zu tun. Das Anpassen des Stapels jedes Mal, wenn eine lokale Variable in den Bereich hinein oder aus ihm herausfiel, würde zu vielen unnötigen Manipulationen des Stapelzeigers führen, ohne Verstärkung.

Ich bin sicher, Sie können herausfinden, was der Rest des Assembly-Codes selbst tut, wenn Sie nicht einen Kommentar posten und ich werde Sie durchgehen.

+0

Eine nette Antwort. Nun, das ist die Art von Antwort, die ich wollte !!! –

+0

Nur für den Fall, wenn jemand wissen muss, wie man den Assembler-Code bekommt .. Verwenden Sie $ gcc -S foo.c –

+0

@Andrew - Pls eine nette Quelle zu lernen Assembler Sprache zu lernen. Ich habe nur Kenntnisse von LC3. Jetzt möchte ich Assembler-Codes für zB Core2Quad verstehen. –

12

Es ist Speicher auf dem Stapel. Es wird nicht vom Heap zugewiesen. Der Stapel würde sich in dieser Schleife nicht ändern.

+0

Aber ich bekomme immer die gleiche Adresse .. Warum? –

+1

Weil es jedes Mal dieselbe Variable ist. –

+5

@Adam: Es ist nicht "wirklich" die gleiche Variable jedes Mal, es ist nur an der gleichen Stelle jedes Mal (in dieser Implementierung und in fast jeder plausiblen Implementierung von C) –

1

Sie sind nicht malloc-ing. Es ist eine Stack-Adresse, also ist es immer dasselbe, weil es immer wieder an der gleichen Stelle des Stacks ist.

2

j ist auf dem Stack zugewiesen, so dass es während eines Aufrufs dieser Funktion immer dieselbe Adresse hat.

Wenn Sie innerhalb dieser Schleife main() aufgerufen haben, würde die "innere" mainj eine andere Adresse haben, da es auf dem Stapel höher wäre.

Siehe Hardware Stack auf Wikipedia für weitere Details.

6

Warum sollte es anders sein? Der Compiler benötigt Platz auf dem Stack, um einen Int zu speichern, und jedes Mal, wenn er durch die Schleife geht, ist der gleiche Platz verfügbar.

Übrigens verwenden Sie überhaupt nicht malloc. j wird auf dem Stapel aufbewahrt.

2

Eigentlich verwenden Sie nicht malloc so was ist das Problem?

Die Variable ist eine lokale Funktion, und ihr Platz ist während der Kompilierung auf dem Stapel reserviert. Warum sollte sie bei jeder Iteration neu zugewiesen werden? Nur weil es in der Schleife deklariert ist?

4

j und ich sind auf dem Stapel zugeordnet, nicht auf dem Heap oder freestore (was ein malloc oder neu erfordern würde). Der Stapel setzt die nächste Variable an eine deterministische Position (die oberste des Stapels) und hat daher immer dieselbe Adresse. Wenn Sie jedoch im optimierten Modus arbeiten, ist die Variable wahrscheinlich nie "dealloced", dh die Stack-Größe ändert sich nicht im gesamten Programm, da es sich nur um verschwendete Zyklen handelt.

-1

Jetzt würden Sie eine Reihe von durchgesickerten Zuweisungen erhalten, da die js nicht für nachfolgende Freigaben gespeichert werden. j würde nicht unbedingt eine zufällige Adresse erhalten, sondern wahrscheinlich nur als eine Sequenz verglichen mit den vorherigen Zuordnungen von j.

Wenn Sie j am Ende der Schleife freigeben würden, könnten Sie das gleiche Verhalten wie zuvor erhalten, abhängig von der Implementierung von malloc und free.

Bearbeiten: Sie können die gedruckten Werte mit diesem Code überprüfen.

+0

Sie müssen nicht j in diesem Fall frei, da es warn 't dynamisch zugewiesen. –

+0

Nun, wenn die Frage von Zeit zu Zeit nicht bearbeitet wurde, um den Anruf zu malloc hinzuzufügen oder zu entfernen, dann wäre die Antwort ein Spaziergang im Park. –

0

Hinweis: Was denkst du, was das tun wird?

#include<stdio.h> 
#include<malloc.h> 

int main() 
{ 
    int i=3; 
    while (i--) 
    { 
     int j = 42; 
     printf("%p\n", &j); 
    } 
    return 0; 
} 
1

Es ist innerhalb der Schleife erklärt, wie Sie sagen, aber es beide geht aus Umfang und ist ‚zerstört‘ am Ende jeder Iteration (das ist es nicht in ihrem Umfang ist und nicht existiert, wenn der Schleifenzustand wird getestet). Daher ist es völlig legitim, dass derselbe Stapelspeicherplatz wiederverwendet wird (in der Tat wäre es ein Fehler, wenn dies nicht der Fall wäre).

0

Wie andere Antworten gesagt haben, vergeben Sie hier nichts als Stapel. Aber selbst wenn Sie den Code wie folgt ändern, wird die Zuordnungsadresse nicht unbedingt geändert.

Dies hängt von der verwendeten libc ab, malloc befindet sich normalerweise dort, aber einige Anwendungen (vor allem Firefox) überschreiben sie für ihre Verwendung (Speicherfragmentierungsprobleme usw.).

#include<stdio.h> 
#include<malloc.h> 

int main() 
{ 
    int i=3; 
    while (i--) 
    { 
     int *j = (int *) malloc(sizeof(int)); 
     printf("%p\n", j); 
     free (j); 
    } 
    return 0; 
} 

Wenn Sie die freie (j) auskommentieren, werden Sie feststellen, dass sich die Adresse von j ändert. Aber abhängig von Ihrer libc kann sich die Adresse des js immer ändern.