Bild Referenz: http://lueersen.homedns.org/Pronix_final/ckurs/ckurs65.html
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!
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:
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:
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);
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:
Steuern werden wie folgend abgerechnet:
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...
:)
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!