ThInf-HOME

  1. Einführung

  2. Dynamische Speicherverwaltung

  3. Beispiel mit malloc()(und Cast Operator)

  4. Beispiel mit realloc()

  5. Zufallsgenerator

  6. Übungsaufgaben


I-PROGRAMMIEREN1 WS 0203 - Vorlesung mit Übung
VL6: Dynamisches Speichermanagement; Zufallsgenerator


AUTHOR: Gerd Döben-Henisch
DATE OF FIRST GENERATION: Oct-27, 2002
DATE OF LAST CHANGE: Nov-4, 2002
EMAIL: Gerd Döben-Henisch



1. Einführung


Bisher haben wir nur den Fall kennengelernt, dass der Speicherbereich einer C-Variablen automatisch eingerichtet (allokiert, 'allocated') wird. Man deklariert z.B. die Variablen

char c, input[10];

char *cp;

int i, rangordnung[15];

int *ip;

und der Compiler 'reserviert' den dazugehörigen Speicher 'automatisch'. Wir haben ferner gesehen, dass man die Hardwareadressen dieser automatisch zugeordneten Speicherbereiche für jede Variable explizit mit dem Adressoperator abfragen kann, also z.B.:

cp = &c;

ip = &i;

Der Nachteil dieses 'automatischen Verfahrens' ist es, dass man schon zu Beginn des Programmstarts wissen muss, dass man Speicher benötigt und wieviel. Man kann diese Speicherstruktur während des Programmlaufs dann nicht mehr ändern. Es gibt aber Situationen, in denen ein Programm auf Umweltanforderungen dynamisch reagieren muss. Hier wäre es dann höchst wünschenswert, wenn das Programm nach Bedarf Speicher reservieren oder auch wieder freigeben kann. Solche Funktionen gibt es in C und sie sollen heute kurz vorgestellt werden.




START

2. Dynamische Speicherverwaltung



Funktionsprototypen

Wirkung

#include <stdlib.h>

size = sizeof(Object);

void *malloc(size_t size);

malloc() reserviert einen Speicherbereich der Grösse size und liefert einen Pointer auf das erste Element dieses Bereiches. Wenn dies nicht gelingt, liefert malloc einen NULL-pointer zurück. Bei size=0 wird kein Speicher reserviert.

void *calloc(size_t elt_count, size_t elt_size);

calloc() reserviert einen Array von elt_count vielen Elementen, wobei jedes Element die Grösse elt_size besitzt. Alle Bits dieses Bereiches werden auf 0 gesetzt und ein Pointer auf das erste Element des Bereiches wird zurückgegeben. Wenn elt_count oder elt_size gleich 0, dann entspricht das Verhalten dem Fall malloc mit size=0.

void *free(void *ptr);

free() gibt Bereiche wieder frei, die zuvor von malloc(), calloc() oder realloc() reserviert wurden. Dazu muss der pointer im Argument einer sein, der zuvor von einer der Funktionen malloc(), calloc() oder realloc() zurückgegeben wurde. Ein NULL-Pointer bewirkt nichts.

void *realloc(void *ptr, size_t size);

realloc() erweitert einen Speicherbereich so, dass die alten Inhalte erhalten bleiben. Wenn es dazu notwendig ist, die alten Inhalte zu kopieren, dann werden diese in den neuen Speicherbereich kopiert, der alte Bereich wird freigegeben und ein neuer Pointer wird zurückgegeben. Übergibt man einen gültigen Pointer mit size=0, dann wird der alte Bereich freigegeben und ein NULL-Pointer zurückgegeben. Ist der neue Bereich kleiner als der alte, dann werden Teile des alten Inhalts gelöscht.



START



3. Beispiel mit malloc() (und Cast Operator)



In der Datei malloc_bsp1.c sagt der Benutzer, wieviele Integerzahlen er eingeben will (= array_number), dann wird ein Speicherbereich von dieser Grösse eingerichtet. Die Adresse des neuen Speicherbereiches wird in dem Zeiger 'ipa' gespeichert:

ipa = (int *)malloc(sizeof(int)* array_number);

Der Ausdruck (int *) vor der Funktion malloc() ist ein sogenannter Cast-Operator. Mittels (TYP) kann man dem Compiler explizit sagen, dass er einen Wert in den angegebenen Typ verwandeln soll. malloc() hat standardmaessig als Rückgabewert den Typ 'void *'. Durch den Cast-Operator (int *) wird der Typ 'void *' in den Typ 'int *' verwandelt, der mit dem Zeiger ipa übereinstimmt, der den Wert der Adresse entgegennimmt.





/*****************************
/*
* malloc_bsp1.c
*
* author: gdh
* first: WS 0102
* last: nov-04, 02
*
* FUNKTIONALITAET:
* -mit malloc() werden Speicherbereiche für Objekte reserviert.
* -Das Objekt ist ein Array vom Typ int, der mit Daten gefuellt wird
*
* compilation: gcc -o  malloc_bsp1  malloc_bsp1.c
* usage:  malloc_bsp1
*
************************/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

main(void) {

  int  *ipa;         /* Pointer auf Array von int */
  int array_number, i;

  /******** START *********/

  printf("Wieviele Datenelemente wollen Sie eingeben? =  ");
  scanf("%d",  &array_number);
 printf("\n\nKontrolle: Sie wollen %d -viele Datenelemente eingeben \n",array_number);

 /*******MALLOC***********/

 ipa = (int *)malloc(sizeof(int)* array_number);
 if (ipa == NULL) fprintf(stderr,"Kein Speicher verfügbar\n");
 else { printf("Adresse des neuen Speicherbereiches ist %u \n",ipa);}


 /**********DATENEINGABE***********/

  i =0;

  while (i < array_number) {

  printf("\n Bitte Eingabe Nr. %d  =  ",i);
  scanf("%d",  &ipa[i]);
    i++;

  }/* End of while */

  /*********ERGEBNISKONTROLLE**************/

 printf("\n Ergebnis-Liste: \n");

  i =0;

  while (i < array_number) {

  printf("Nr. %d  = %d \n ",i,ipa[i]);
    i++;

  }/* End of while */

}




START



4. Beispiel mit realloc()



In der Datei realloc_bsp1.c wird gezeigt, wie man mit realloc() einen einmal mit malloc() reservierten Speicherbereich in nachfolgenden Anwendungen vergroessern kann. Dabei ist allerdings zu beachten, dass man den alten Speicherbereich bei der Groessenangabe berücksichtigt:

ipa = (int *)realloc(ipa, sizeof(int) * (array_number + array_number_old));

In diesem Aufruf enthält die Variable array_number_old die alten Elemente und die Variable array_number die neuen.





/************************************************
*
* realloc_bsp1.c
*
* author: gdh
* first: ws 0102
* last: nov-04, 02
*
* FUNKTIONALITAET:
* -mit realloc() werden Speicherbereiche für Objekte reserviert.
* -Das Objekt ist ein Array vom Typ int, der mit Daten gefuellt wird
* -Wird zusaetzlicher Speicherbereich benoetigt, kann mittels realloc()
*  zusaetzlicher Speicher angefordert werden
*
* KOMPILIEREN:  gcc -o realloc_bsp1 reallo_bsp1.c
* AUFRUF: realloc_bsp1
*
************************/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

main(void) {


  int  *ipa;         /* Pointer auf Array von int */
  int array_number, array_number_old, i;


  /******** START *********/

  printf("Wieviele Datenelemente wollen Sie eingeben? (Ende mit 0) =  ");
  scanf("%d",  &array_number);

 printf("\n\nKontrolle: Sie wollen %d -viele Datenelemente eingeben \n",array_number);

 if ( array_number > 0 ) {

 /*******MALLOC***********/

 ipa = (int *)malloc(sizeof(int)* array_number);
 if (ipa == NULL) fprintf(stderr,"Kein Speicher verfügbar\n");
 else { printf("Adresse des neuen Speicherbereiches ist %u \n",ipa);}


 /**********DATENEINGABE***********/

  i=0;

  while (i < array_number) {

  printf("\n Bitte Eingabe Nr. %d  =  ",i);
  scanf("%d",  &ipa[i]);
    i++;

  }/* End of while */

  /*********ERGEBNISKONTROLLE**************/

 printf("\n Ergebnis-Liste: \n");

  i =0;

  while (i < array_number) {

  printf("Nr. %d  = %d \n ",i,ipa[i]);
    i++;

  }/* End of while */

 }/* Ende if erste Abfrage */


 /*********************************************/
 /************WEITERE ABFRAGEN *************/

 array_number_old = array_number;  /* Beruecksichtigen der alten Daten */

 while ( array_number > 0 ) {

  /******** START *********/

  printf("Wieviele Datenelemente wollen Sie zusaetzlich eingeben? (Ende mit 0) =  ");
  scanf("%d",  &array_number);
 printf("\n\nKontrolle: Sie wollen %d -viele Datenelemente zusaetzlich eingeben \n",array_number);

 if (array_number > 0 ) {

 /*******REALLOC***********/

 ipa = (int *)realloc(ipa, sizeof(int) * (array_number + array_number_old));
 if (ipa == NULL) fprintf(stderr,"Kein Speicher verfügbar\n");
 else { printf("Adresse des neuen Speicherbereiches ist %u \n",ipa);}


 /**********DATENEINGABE***********/

  i =0;

  while (i < array_number) {

  printf("\n Bitte Eingabe Nr. %d  =  ",i);
  scanf("%d",  &ipa[array_number_old+i]);
    i++;

  }/* End of while */

  /*********ERGEBNISKONTROLLE**************/

 printf("\n Ergebnis-Liste: \n");

  i =0;

  while (i < array_number_old+array_number) {

  printf("Nr. %d  = %d \n ",i,ipa[i]);
    i++;

  }/* End of while */

  array_number_old += array_number;

 }/* Ende if wegen terminierender 0 */
 }/* End of while array_number2 > 0 */

}






START

5. Zufallszahlengenerator



In der Standardbibliothek (siehe stdlib.h) wird in C eine Zufallsfunktion rand() zur Verfügung gestellt. Diese holt sich aus einer intern erzeugten Reihe von Pseudozufallszahlen jeweils eine Pseudozufallszahl heraus. Mittels der Funktion srand(unsigned seed) kann man unterschiedliche Zufallsreihenfolgen anstossen.

Das Programm random1.c zeigt, wie man diese Funktionen benutzen kann.

Wichtig ist, dass man bei Aufruf des Programms drei Argumente auf der Kommandozeile übergeben muss:

Die entscheidende Zeile zur erzeugung einer Zufallszahl ist dann die folgende:

r=1+(int)( upper*rand() /(RAND_MAX+1.0 ) );

die Funktion rand() erzeugt eine Zufallszahl zwischen 1 und RAND_MAX. Diese wird durch die benutzte Formel auf den Bereich zwischen 1 und upper transformiert.





/****************************
*
* random1.c
*
* author: gdh
* first: ws 0102
* last: nov-4, 02
*
* idea: Ein Zufallsgenerator mit oberer Schranke
*       Man uebergibt auf der Kommandozeile Argumente:
*       SEED = Zahl zum starten einer neuen Zufallsfolge
*       UPPER = Groesste Zufallszahl, die erzeugt werden soll
*       MAX_RUN = Anzahl der zu erzeugenden Zufallszahlen
*
* KOMPILIEREN: gcc -o random1 random1.c
*
* Aufruf: random1  SEED UPPER MAX_RUN
*
*********************************/


#include <stdio.h>
#include <stdlib.h>


main (int argc, char *argv[]){

long int  i;
 long int seed, MAX_RUN;
 float  upper;
 int r;

 /*********** UERBERNAHME DER ARGUMENTE AUS DER KOMMANDOZEILE *****************/

 seed=atol(argv[1]);
 upper=atof(argv[2]);
 MAX_RUN=atol(argv[3]);

 /* KONTROLLAUSDRUCK */

 printf("SEEDINPUT = %ld\n",seed);
 printf("UPPER = %f\n", upper);
  printf("MAX_RUN = %d\n",MAX_RUN);

  /********* AUSWAHL EINER ZUFALLSZAHLENFOLGE DURCH SEED ***********/

  srand((unsigned) seed);

  /* ERZEUGEN VON MAX_RUN-VIELEN ZUFALLSZAHLEN */
  /* Da rand() eine Zahl zwischen 1 und RAND_MAX liefert, muss diese Zahl
   * auf den gewünschten Bereich umgerechnet werden. Die 'Numerical Recipes in C'
   * empfehlen die untenstehende Formel: */

  for (i=0;i<MAX_RUN+1;i++){

    r=1+(int)( upper*rand() /(RAND_MAX+1.0  ) );
    printf("r = %d\n",r);}


}

START

6. Übungsaufgaben

  1. Bilden sie ein Team von 3 Mitgliedern


  2. Erstellung Sie gemeinsam einen Texte mit Namen und Matr.Nr. der AutorenInnen. Abgabe einer Kopie des Textes an den Dozenten vor Beginn der Vorlesung am Di (1 Woche nach Aufgabenstellung). Falls die Aufgabe ausführbaren Programmcode enthält wäre die zusätzliche Übergabe des Quelltextes auf Diskette oder per email wünschenswert, aber nicht verbindlich. Im Falle von Programmcode muss im Programmtext auch nochmals Name und Matr.Nr. erscheinen.


  3. Präsentation der Lösung als Team vor der gesamten Gruppe. Präsentationszeit (abhängig von der Gesamtzahl der Teams) 3-5 Min. Mögliche Punkte: 1-2 im Normalfall. Bei besonders guten Leistungen bis zu 3 Punkten.


  4. Versuchen Sie in dem Text Antworten auf folgende Aufgaben zu formulieren:


  5. Schreiben Sie ein Programm, das den Benutzer fragt, ein wie grosses N x N -Feld vom Typ char es anlegen soll und das den Wert jeder Zelle des Feldes auf '_' setzt (Benutzung von malloc()).


  6. Lassen Sie den Zufallsgenerator so lange laufen, bis alle Zellen statt den Anfangswert '_' den Wert '#' aufweisen.Druken Sie die Veränderungen nach jedem Ereignis auf dem Bildschirm aus.


  7. Stellen Sie fest, wieviele Ereignisse benötigt werden, bis alle Felder zufällig mit dem Wert '#' belegt wurden. Gibt es Unterschiede je nachdem, welchen Wert Sie für 'seed' benutzt haben?


  8. Automatisieren Sie Ihr Programm so, dass es für gleiche Werte N x N mit unterschiedlichen seed-Werten die Anzahl der benötigten Durchläufe zählt.



START