Programmieren in C - Dynamische Speicherzuordnung

in #programmieren6 years ago

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!

Sort:  

@drifter2, I gave you an upvote on your post!

If you are interested in claiming free Byteballs ($10+), just for having a Steem account, please visit this post for instructions: https://steemit.com/steem/@berniesanders/do-you-want-some-free-byteballs

I like the C language but the brackets can make it hard to know if you have closed off every function properly.

C++ is also a good language but it is also a good way to write a high density of computer bugs.


Google Übersetzer

Ich mag die Sprache C, aber die Klammern können es schwierig machen zu wissen, ob Sie jede Funktion ordnungsgemäß beendet haben.

C ++ ist auch eine gute Sprache, aber es ist auch eine gute Möglichkeit, eine hohe Dichte von Computer-Bugs zu schreiben.

Congratulations @drifter2! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

Award for the total payout received

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do you like SteemitBoard's project? Then Vote for its witness and get one more award!

Coin Marketplace

STEEM 0.22
TRX 0.25
JST 0.039
BTC 95470.30
ETH 3313.37
USDT 1.00
SBD 3.15