/* In C lassen sich auch die Adressen der eingeführten Datenobjekte auswerten. Adressen dürfen berechnet werden (ADRESSARITHMETIK). Variablen, die Adressen speichern, heissen ZEIGER (engl. Pointer). Die Adressbelegung in einem Programm erfolgt bei Betriebssystemen wie Unix, Linux oder Windows nach dem Konzept der VIRTUELLEN SPEICHERVERWALTUNG. Die Struktur des virtuellen Speichers ist wie folgt: GLOBALE VARIABLEN: werden vom Compiler ab einer bestimmten Anfangsadresse A1 statisch allokiert. Die Zuordnung von globalen Variablen zu Adressen erfolgt von A1 an "nach unten", d.h. an Adressen, die unterhalb von A1 liegen. HEAP: Heap-Speicher wird "oberhalb" von A1, d.h bei Adressen > A1 angelegt. Jede weitere Heap-Allokation wird mit den nächst höheren Adressen versehen. STACK: Von der maximalen fuer Daten verfuegbaren Adresse abwärts werden Stackdaten angelegt, sobald der zugehörige Block { ... } erreicht wird. Im Stack liegen (1) die lokalen Variablen des Blocks (2) die Funktionsparameter, wenn der Block zu einem Funktionsaufruf gehoert. Der einem Block zugeordnete Stackbereich wird freigegeben, sobald der Block verlassen wird. Er kann dann fuer die Variablen eines neuen Blocks wiederverwendet werden. Mit den Variablen des nachfolgenden Beispiels ergibt sich folgendes Schema fuer die Speicheraufteilung: ========================== -------------------------- G-8 gMyFloat -------------------------- G-4 gMyInt ========================== G: STARTADRESSE GLOBALE VARIABLEN ========================== G': STARTADRESSE HEAP pHeapVar1 (1000 Bytes) -------------------------- G'+1000 pHeapVar2 (4000 Bytes) -------------------------- ... Heapadressen werden groesser mit wachsendem Heap ========================== ... Stackadressen werden kleiner mit wachsendem Stack -------------------------- S'-16 j -------------------------- S'-12 i -------------------------- S'-8 mi -------------------------- S'-4 mf -------------------------- S' Bei Aufruf von f(): Verwaltungsinformation fuer Funktionsaufruf (Ruecksprungadresse etc) -------------------------- S-8 myFloat -------------------------- S-4 myInt ========================== S: STARTADRESSE STACK (maximale virtuelle Adresse) */ #include /* globale Variablendeklarationen */ int gMyInt; float gMyFloat; /*-------------------------------------------------------------*/ void g(int mi, float mf) { int i; int j; printf("Adresse von mf in g() = %012u\n", (unsigned int)&mf); printf("Adresse von mi in g() = %012u\n", (unsigned int)&mi); printf("Adresse von i in g() = %012u\n", (unsigned int)&i); printf("Adresse von j in g() = %012u\n", (unsigned int)&j); } /*-------------------------------------------------------------*/ void f(int mi, float mf) { int i; int j; printf("Adresse von mf in f() = %012u\n", (unsigned int)&mf); printf("Adresse von mi in f() = %012u\n", (unsigned int)&mi); printf("Adresse von i in f() = %012u\n",(unsigned int)&i); printf("Adresse von j in f() = %012u\n",(unsigned int)&j); g(1,2.0); } /*-------------------------------------------------------------*/ int main(int argc, char **argv) { int myInt; float myFloat; int *pInt; float *pFloat; /* mit malloc() [CLIB-Funktion] wird zur Laufzeit (d.h. dynamisch) Speicher auf dem Heap allokiert. Als Parameter erhält malloc() die gewuenschte Speichergroesse in Bytes. Malloc gibt einen Pointer auf den Anfang des allokierten Speichers zurueck. malloc() gibt einen void-ponter, d.h. einen pointer ohne Datentyp zurueck (void-pointer machen Byte-Adressierung.) Damit man den Speicher mit einem bestimmten Datentyp strukturieren kann, muss der void-pointer auf diesen Datentyp gewandelt werden. Die Wandlung heisst CAST; sie wird mit der Syntax () definiert. */ char *pHeapVar1 = (char *)malloc(1000); int *pHeapVar2 = (int *)malloc(1000); /** Einlesen von Daten: scanf kopiert Daten von der Konsole an die Adresse, die in der Variablenliste angegeben ist. Die Eingabe durch Tastatur wird von scanf() gemaess Formatangabe (zB "%d") gewandelt */ scanf("%d", &myInt ); printf("\n gelesen wurde %d\n", myInt ); /* Adressen der globalen Variablen */ pInt = &gMyInt; printf("Adresse von gMyInt = %012u\n",(unsigned int)pInt); pFloat = &gMyFloat; printf("Adresse von gMyFloat = %012u\n",(unsigned int)pFloat); /* Adressen der Stack-Variablen in main() */ pInt = &myInt; printf("Adresse von myInt = %012u\n",(unsigned int)pInt); pFloat = &myFloat; printf("Adresse von myFloat = %012u\n",(unsigned int)pFloat); printf("Adresse von pInt = %012u\n",(unsigned int)&pInt); printf("Adresse von pFloat = %012u\n",(unsigned int)&pFloat); /* Ausgabe der Heap-Adressen */ printf("Adresse von pHeapVar1 = %012u\n", (unsigned int)pHeapVar1); printf("Adresse von pHeapVar2 = %012u\n", (unsigned int)pHeapVar2); f(myInt,myFloat); g(13,4.5); }