Programmieren in C - Dynamische Speicherzuordnung

Words
1339
Reading
6 min
Listen
Play
8y

Bild Referenz: http://lueersen.homedns.org/Pronix_final/ckurs/ckurs65.html

Intro

    Hallo, ich bins @drifter2! Der heutige Artikel ist am englischen Artikel: "C Dynamic Memory Allocation" basiert. Also werden wir vieles alles über die Dynamische Speicherzuordnung oder Zuweisung lernen. Ist nicht viel mehr zu lasen also fangen wir mal an!

Dynamischer Speicher

    Bei Programmen brauchen wir meistens nicht nur statische, sondern auch dynamische Variablen. Die dynamischen Variablen, Felder, Zeiger usw. werden im so genannten Dynamischen Speicher (auch Heap) eingespeichert. Der Heap ist ein Speicherbereich der in der Laufzeit eines Programms angefordert und freigegeben kann. Nach der Freigabe und insbesondere nach der Programmausführung sollte man diesen Speicher wieder "reinigen", etwas das mit der Hilfe von Speichereinigungs-Funktionen (die meistens automatisch abgerufen werden ) gemacht wird. Durch die Benutzung von dem Heap und dem so genannten Stack (der statische Speicher) hat der Programmier jetzt viele Arten an Speichern zu Verfügung. Der Unterschied zum Stack besteht darin, dass beim Stack angeforderte Speicherabschnitte in der umgekehrten Reihenfolge wieder freigegeben werden müssen, in der sie angefordert wurden. Dies schränkt die Wiederverwendung nicht länger benötigter Stack-Bereiche ein. 

Der Dynamische Speicher verspricht:

  • Hohe Geschwindigkeit
  • Effiziente Speichernutzung
  • Geringer Verwaltungsaufwand

Speicherzuweisung/Zuordnung in C

    Um diese "Fähigkeit" von C zu benutzen müssen wir die stdlib.h Standardbibliothek von C in unser Programm importieren. Diese Bibliothek enthält Funktionen für die Dynamische Speicherzuweisung, wo meistens ein Zeiger zu dieser Speicher-Stelle zurückgegeben wird. Die wichtigsten Funktionen sind:

  • malloc(size) -> das zur Speicherzuteilung dient
  • realloc(memory, size) -> das eine bestehende Speicherzuweisung erneut zuweist
  • calloc(elements, size) -> das eine "reine" Speicherzuteilung ausführt (alles auf 0 initialisiert)
  • free(pointer) -> das den Speicher wieder freigibt, oder reinigt

    Sagen wir mal wir wollen ein Dynamisches Ganzzahl (Integer) Feld deklarieren. Dazu deklarieren wir einen Pointer wie folgend:

int *A;

     Sagen wir mal wir wollen N-Ganzzahlen und brauchen keine Initialisierung auf 0, das heisst das malloc() uns ausreicht. Da malloc() einen void- Zeiger zurückgibt ist der Code dazu:

A = (int*) malloc(N*sizeof(int));

    Wie ihr sehen könnt müssen wir eine Datentyp-Umwandlung ausführen so das der void-typ zu einem Integer-typ wird. Der Parameter enthält die sizeof() Funktion die uns die Größe von Ganzzahlen gibt und das wir dann mit N multiplizieren so das wir einen Speicher zuteilen der für N-Ganzzahlen ausreicht. 

     Nach der Zuweisung sollten wir jetzt den Zeiger checken ob er NULL ist, ein Wert der Fehler anzeigt (wie z. B. kein Freier Speicher). Das geht wie folgt:

if(A == NULL){
    // Fehler behandeln
}

    Sagen wir jetzt wir brauchen M Ganzzahlen (die mehr oder weniger als N sind). Dazu benutzen wir jetzt die realloc() Funktion. Der Code ist wie folgend:

A = realloc(A, M*sizeof(int));

     Wenn M kleiner ist, werden nur die M von den N Ganzzahlen "im Speicher bleiben" (die bleiben schon, aber sind halt eigentlich nicht mehr "richtig" abgespeichert). Wenn M größer ist dann bleiben die N ganz normal abgespeichert und behalten auch ihren Wert und es werden einfach mehr Speicherplätze freigegeben.

     Nachdem wir fertig sind machen wir am Ende noch eine Speicherreinigung indem wir free() benutzen...

free(A);

Beispielprogramm

Gehen wir jetzt mal in ein komplettes Programm, so das ihr das ganze besser versteht...

     Wir wollen unendlich viele Steuerinformationen (Tax informations) eingeben bis eine Tax-ID von "00000" eingegeben wird.

Diese Informationen sind:

  • ID (Ganzzahl aus 5 Ziffern), 
  • Name (Zeichenkette), 
  • Nachname (Zeichenkette), 
  • Geburtstag als Zeichenkette (Alter muss mindestens 18 sein, das wir "extrahieren"), 
  • Kategorie (self_employed, wage_earner),
  • Einkommen als Float
  • Steuern als Float

Steuern werden wie folgend abgerechnet:

  1. self_employed 30% ihres Einkommens
  2. wage_earner Gestaffelt  (Staggered charge):
  • 0% bis 10.000
  • 20% von 10.000 bis 30.000
  • 30% von 30.000 und aufwärts

    Nachdem der unendliche Loop stoppt werden die Informationen als eine Matrix ausgedruckt. Da die Nummer der Steuerzahler unbekannt ist müssen wir natürlich Dynamische Felder (Arrays) benutzen, genau wie das in diesem Artikel beschrieben wurde.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Y 2018
int main(){
    int *ids; //dynamic array of ids
    char **names; //dynamic array of names
    char **surnames; //dynamice array of surnames
    int *ages; //dynamic array of ages
    char *category; //dynamic array of categories of the taxpayers
    float *incomes; //dynamic array of incomes
    float *taxes; //dynamic array of taxes
 
    char id[6]; // als Zeichenkette lesen fuer eine Maximale Laenge aus 5 Zeichen 
    char name[30], surname[30]; // Temporaere name und surname Variablen
    char date[11]; // Um den Geburtstag als DD/MM/YYYY zu lesen
    char *p; // Zeiger um "date" zu gearbeiten
    int day, month, year; // Gesplitterter Geburtstag
    float income, tax; // um Einkommen einzufuegen und Steuern zu berechnen
    char cat; //temporaere Kategorie
    
    int i; //loop Variabel
    int flag; // Flagge zum checken
    int count = 0; // Zahler der Steuerbezahler
    
    printf("TAX PROGRAMM:\n");
 while(1){
     
     // ID einfuegen (5 Ziffern)
     while(1){
         flag = 1; // wenn 0, muss man wieder einfuegen
              printf("id: ");
                 scanf("%5s", id);
         fflush(stdin);
                        // Laenge check
         if( strlen(id) < 5) flag = 0;
         else{
                                // Nummern-Check
             for(i = 0; i < 5; i++){  
                   if( (id[i]<'0') || (id[i]>'9') ){
                       flag = 0;
                       break;
                 }
          }
         }           
         if(flag == 1) break;
     }
     if(atoi(id) == 0){ // Stoppen wenn "00000"
         break; 
     }
        // fuer den ersten malloc()
     if(count == 0){
         ids = (int*)malloc(1*sizeof(int));
     }
               // fuer jeden anderen 
     else{
         ids = (int*)realloc(ids, (count+1)*sizeof(int));
     }
     ids[count] = atoi(id);
     
     // Namen und Nachnamen einfuegen    
          printf("name: ");
          scanf("%29s", name);
          fflush(stdin);
              
          printf("surname: ");
          scanf("%29s", surname);
          fflush(stdin);
          
                // fuer den ersten
       if(count == 0){
         names = (char**)malloc(1*sizeof(char*));
         surnames = (char**)malloc(1*sizeof(char*));
     }
                // fuer jeden anderen 
     else{
         names = (char**)realloc(names, (count+1)*sizeof(char*));
         surnames = (char**)realloc(surnames, (count+1)*sizeof(char*));
     }
       names[count] = (char*)malloc((strlen(name) + 1) * sizeof(char)); //allocate memory for name
       strcpy(names[count], name);
       surnames[count] = (char*)malloc((strlen(surname) + 1) * sizeof(char)); //allocate memory for surname
       strcpy(surnames[count], surname);
       
       // Geburtstag einfuegen
     while(1){
              flag = 1; // bei 0 nochmal einfuegen
         printf("DD/MM/YYYY: ");
         scanf("%10s",date);
         fflush(stdin);
         day = atoi(date);
         p=strchr(date,'/');
         p=p+1;
         month=atoi(p);
         p=strchr(p,'/');
         p=p+1;
         year=atoi(p);           
             // Tages-check
         if( (day<=0) || (day>31 ) ) flag = 0; 
             // Monats-check
         if( (month<=0) || (month>12) ) flag = 0; 
             // Jahres-check (easy 18)
         if (year > Y - 18) flag = 0;
                        // bei '1' aus dem loop rausgehen
         if(flag == 1) break;
       }
       if(count == 0){
         ages = (int*)malloc(1*sizeof(int));
     }
     else{
         ages = (int*)realloc(ages, (count+1)*sizeof(int));
     }
                // Jahr ins Feld abspeichern
       ages[count] = Y - year;
       
       // Einkommen einfuegen
       while (1){ // muss positiv sein
              printf("yearly income: ");
              scanf("%f" ,&income);
              fflush(stdin);
           if(income > 0) break;
          }
          if(count == 0){
         incomes = (float*)malloc(1*sizeof(float));
     }
     else{
         incomes = (float*)realloc(incomes, (count+1)*sizeof(float));
     }
     incomes[count] = income;
     
     // Kategorie
     while(1){
         printf("Are you self employer(s) or wage earner(w): ");
         scanf("%c", &cat);
         fflush(stdin);
         if(cat == 's' || cat == 'w') break;
     }
     if(count == 0){
         category = (char*)malloc(1*sizeof(char));
     }
     else{
         category = (char*)realloc(category, (count+1)*sizeof(char));
     }
     category[count] = cat;
     
     // Steuern berechnen
     if(cat == 's'){ //self employed
         tax = income * 0.3;
     }
     else{
         if(income <= 10000){
             tax = 0;
         }
         else if(income > 10000 && income <= 30000){
             tax = 0 + (income - 10000) * 0.2;
         }
         else{ //if > 30000
             tax = 0 + 20000*0.2 + (income - 30000) * 0.3;
         }
     }
     printf("You have to pay %.2f tax!\n", tax);
     
     if(count == 0){
         taxes = (float*)malloc(1*sizeof(float));
     }
     else{
         taxes = (float*)realloc(taxes, (count+1)*sizeof(float));
     }
     taxes[count] = tax;
     
     count++; //increment count         
 }
 
 // Informationen ausdrucken
 printf("TAXPAYERS:\n");
 printf("ID\tNAME\tSURNAME\t\tAGE\tCATEGORY\tINCOME\t\tTAXES\n");
 for(i = 0; i < count; i++){
     printf("%d\t%s\t%s\t%d\t", ids[i], names[i], surnames[i], ages[i]);
     if(category[i] == 's') printf("Self employed\t");
     else printf("Wage earner\t");
     printf("%.2f\t%.2f\n", incomes[i], taxes[i]);               
 }
 // es ist schwer alles in einer Linie auszudrucken
       // also ist es besser in 2-3 Teile zu splitten...
}

Bin gerade im Urlaub also gibt es keine Programmausführung mit der Konsole...

:)

Referenzen

  1. https://de.wikipedia.org/wiki/Dynamischer_Speicher
  2. http://cppstudio.com/de/post/9088/
  3. http://www.c-howto.de/tutorial/arrays-felder/speicherverwaltung/

Vorherige Artikel

Einführung -> Programmiersprachen, die Sprache C, Anfänger Programme

Felder ->  Felder, Felder in C, Erweiterung des Anfänger Programms

Zeiger, Zeichenketten und Dateien -> Zeiger, Zeichenketten, Dateiverarbeitung, Beispielprogramm

  Und das war's dann auch mit diesem Artikel und ich hoffe ich hab alles verständlich erklärt. Νächstes mal werden wir über Strukturen (structs) und das Switch-Case Statement reden. Ich will auch einen Artikel über Funktionen machen demnächst...

Ciao!

Programmieren in C - Dynamische Speicherzuordnung | Ecency