#33-36 Stack,Dynm Speicher,Zeierari Flashcards
nenne alle 4 speicherbereiche bei einem C programm
programmspeicher
-übersetzter programmcode (maschinensprache)
stack
-lokale variablen, funktionsparameter, rücksrungadressen
globaler speicher
-glabale variablen, statische variablen
heap
-dynamische speicherverwaltung
erläutere den speicherort stack
stapelspeicher zum speichern von lokalen variablen, funktoinsparametern und rücksprungadressen.
- Speicher mit fixer Größe
- Wird beim Start des Prozesses initialisiert
- Effiziente “Stapel”-Datenstruktur
4
int manyNumbers[100000];
manyNumbers[0] = 1;
manyNumbers[1] = 2;
int count = 2; Schlechte Idee! - Aber: Der Stack ist begrenzt!
- Risiko: Stack overflow
was passiert beim stack overflow?
- Ein Stack overflow tritt auf, wenn der Stack-Speicher vollständig verbraucht
wurde und weiterer Speicher reserviert werden soll: - Lokale Arrays mit zu vielen Einträgen
- Rekursive Funktionen, die nicht oder zu spät terminieren
erläutere den speicherort halde (Heap)
- Großer, unstrukturierter Arbeitsspeicher
- Erlaubt die dynamische Reservierung von “beliebig” viel Speicher
zur Laufzeit. - Zugriff nur über Zeiger möglich
- Nicht automatisch verwaltet
- Verwaltung liegt in der Verantwortung des/der Programmier:in!
was macht der operator sizeof?
- Gibt die Größe eines Ausdrucks oder Datentyps zurück.
- Rückgabetyp: size_t (vorzeichenlose Ganzzahl)
- Regeln:
- sizeof(char) = 1 (auch für signed/unsigned)
- Ausdrücke werden nicht ausgewertet (z.B. sizeof p++)
Daher: Keine Seiteneffekte möglich! - sizeof berücksichtigt Speicherausrichtung.
- Rückgabe hängt stark von Plattform und Compiler ab!
bsp.:
size_t s = sizeof
size_t s = sizeof()
was macht die funktion malloc?
- Syntax:
#include
void *malloc(size_t size) - malloc reserviert Speicher der Größe size.
- Parameter size: Größe des zu reservierenden Speichers in Byte.
- Speicher wird nicht initialisiert.
- Rückgabewert:
- Zeiger auf Anfang des Speicherblocks
- NULL, wenn Fehler bei der Speicherreservierung auftritt
- Rückgabewert muss geprüft werden!
was macht die funktion calloc?
- Syntax:
#include
void *calloc(size_t count, size_t size) - calloc reserviert Speicher der Größe size * count.
- Parameter
- count: Anzahl der Feldelemente
- size: Größe eines Feldelements
- Speicher wird mit 0 initialisiert.
- Rückgabewert:
- Zeiger auf Anfang des Speicherblocks
- NULL, wenn Fehler bei der Speicherreservierung auftritt
- Rückgabewert muss geprüft werden!
wie geht das freigeben von speicher?
- Jeder dynamisch reservierte Speicher muss manuell freigegeben werden!
- Sonst: Memory Leak
- Kein automatisches Freigeben, lediglich beim Programmende
- Funktion:
#include
void free(void *ptr); - Gibt einen (z.B. mit malloc) reservierten Speicherbereich bei ptr
wieder frei. - Undefiniertes Verhalten, wenn:
- ptr wurde nicht zuvor reserviert (durch malloc, calloc, realloc).
- ptr wurde bereits durch free freigegeben.
wie vergrößert man ein feld?
- Vorgehensweise bei der Feldvergrößerung
- Gegeben: Feld a mit m Elementen
- Ziel: Feld a mit n Elementen, n > m
- Erzeuge mit calloc Feld b mit n Elementen
- Kopiere die Elemente des Feldes a an die ersten m Positionen des Feldes b
(Schrittweise mit einer for-Schleife oder per memcpy) - Speicher für Feld a freigeben (Befehl free)
- Setze Zeiger a auf Beginn des Feldes b
(Anweisung: a = b;)
wie geht das Einfügen in Feldern?
- Gegeben: Feld a mit n Elementen
- Ziel: Einfügen von Element e an Index i
- Vorgehen:
1. Erzeuge mit calloc Feld b mit n+1 Elementen
2. Kopiere die Elemente a[0, …, i-1] nach b[0, …, i-1]
3. Kopiere die Elemente a[i, …, n-1] nach b[i+1, …, n]
4. Setze b[i] = e
5. Speicher für Feld a freigeben (Befehl free)
6. Setze Zeiger a auf Beginn des Feldes b
(Anweisung: a = b;)
was bedeutet realloc? was ist die funktion?
- „Re-allokierung“
- realloc erlaubt, die Größe von reserviertem Speicher zu ändern:
void* realloc (void* ptr, size_t size); - Verändert die Größe des Speicherbereichs, auf den ptr zeigt,
auf die neue Größe size. - Rückgabe:
- Anfang des vergrößerten/verkleinerten Blocks
(kann identisch sein zu ptr, muss es aber nicht) - NULL im Fehlerfall
was ist der hintergrund von speicherarten?
- Programmspeicher
- Globaler Speicher
- Speicher wird beim Programmstart angelegt und erst am Programmende wieder freigegeben
- Speicherung von globalen Variablen und statischen lokalen Variablen
- Stack (Automatischer Speicher)
- Wird „bei Bedarf“ automatisch erzeugt und freigegeben
- Speicherung von Funktionsargumenten und nichtstatischen lokalen Variablen
- Interne Realisierung als Stack (Stapel)
- Gesamtspeicherbereich für Stack wird bei Programmstart reserviert
- Wenn Stack bei Funktionsaufruf voll (stack overflow): Programmabbruch
- Heap (dynamischer Speicher, freier Speicher)
- Volle Kontrolle durch Programmierer
- Explizite Anforderung des Speicherbereichs mit malloc/calloc/realloc
- Explizite Freigabe des Speicherbereichs mit free
lokale vs. dynamische arrays
- Deklaration
- Nach der Deklaration int a[100] zeigt a auf den Anfang eines
Speicherbereichs für Integer-Zahlen. - Nach der Deklaration int *z = calloc(100, sizeof(int)) zeigt z ebenfalls
auf den Anfang eines Speicherbereichs für Integer-Zahlen. - Speicher
- Für a wird der Speicher auf dem Stack oder im globalen Speicher automatisch
reserviert. - Für z muss der Speicher manuell auf dem Heap reserviert (und später
freigegeben) werden, z.B. mit calloc bzw. free - Zugriff
- für beide Arten von Arrays gilt:
- mit *a bzw. *z wird auf das erste Element zugegriffen
- mit a[i] bzw. z[i] wird auf das Element mit Index i zugegriffen
zeigerarithmetik
- Ein Zeiger speichert eine Adresse im Arbeitsspeicher.
- Zeiger sind typisiert
(außer void*, dort auch keine Adressarithmetik möglich) - Addition / Subtraktion von Zeiger mit int-Zahl i
- Adresse wird um i Elemente (nicht Byte!) verändert
- Die Adresse wird also um i * sizeof(datentyp) verändert
- Was ist die Ausgabe von folgendem Programm?
int a[5] = {11, 23, 34, 48, 59};
int z;
z = a;
printf(“z = %d *a = %d\n”, *z, a);
printf(“(z+2) = %d *(a+2) = %d\n”, *(z+2), (a+2));
printf(“z+2 = %d *a+2 = %d\n”, *z+2, *a+2); - Fazit der vorherigen Folie: z[n] entspricht *(z + n)
- Inkrement und Dekrement: z++, z–, ++z, –z
- analog zu Operatoren auf Integer, es wird aber elementweise
(sizeof(datentyp)) addiert/subtrahiert. - Was gibt folgendes Programm aus?
int array[5] = {1, 3, 4, 8, 9};
int *ptr = array, result = 0;
for(int i = 0; i < 5; i++)
{
result += *ptr;
ptr++;
} - Subtraktion von Zeigern z1 – z2
- nur sinnvoll, wenn beide zu gleichem Speicherbereich gehören
- Ergebnis: Anzahl von Elementen zwischen den Zeigern (Typ int)
- Was macht das folgende Programm?
int wasMacheIch(char *z)
{
char z2 = z;
while(z2) {
z2++;
}
return z2 - z;
}
int main()
{
char str[101];
printf(“Eingabe: “);
scanf(“%100s”, str);
printf(“Ergebnis: %d\n”, wasMacheIch(str));
return 0;
}
Mehrdimensionale felder und zeiger
● Felder können mehrdimensional sein, z.B. int a[5][5]
● Auch mehrdimensionale Arrays können auf Zeiger (int *z) gelegt werden,
Speicherberechnung muss dann manuell angepasst werden.
Anwendungsbeispiel: beliebig große Matrizen ausgeben
void printMatrix(int *matrix, int m, int n)
{
int i, j;
for(i = 0; i < m; i++) {
for(j = 0; j < n; j++) {
printf(“%4d “, (matrix + in + j));
}
printf(“\n”);
}
}
int main()
{
int matrix[3][4] = { { 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12} };
printMatrix(matrix, 3, 4);