1 Theorie | |
→ | 1.2 Häufig auftretende Problemstellungen |
Überblick |
Funktionen |
Beispiel |
Coding Standards |
Empfehlungen |
Aufgaben |
Mitunter ist die Größe von Objekten im Hauptspeicher noch nicht bekannt, wenn das Programm compiliert wird sondern kann erst berechnet werden, wenn das Programm läuft.
Ein Beispiel hierfür wäre die Berechnung von statistischen Daten wie Mittelwert und Standardabweichung aus n Werten. Ein entsprechendes Programm würde zunächst die Anzahl der Werte abfragen, dann die Werte selbst einlesen und danach die Berechnung vornehmen.
Wird ein statisches Feld mit fester Feldgröße benutzt, können folgende Abweichungen von Feldgröße und Werte-Anzahl auftreten:
Einen Ausweg bietet die dynamische Speicherverwaltung. Hierbei kann ein Prozess zur Laufzeit Speicher anfordern, wenn er die benötigte Größe kennt.
Zum Anfordern von Speichern stehen folgende zwei Funktionen zur Verfügung:
void *calloc(size_t count, size_t elsize); void *malloc(size_t size);
Die Funktion calloc() fordert Speicher für ein Feld von
count Elementen an, wobei jedes Element elsize Bytes
groß ist.
Bei Erfolg setzt calloc() alle Bytes des Speicherbereiches
auf 0 und gibt einen Zeiger auf den Anfang des Speicherbereiches
zurück.
Bei einem Fehler - z.B. wenn kein Speicher mehr verfügbar ist -
gibt calloc() NULL zurück.
Die Funktion malloc() fordert size Bytes an und
gibt bei Erfolg einen Zeiger auf den Anfang des Speicherbereiches
zurück. Der Speicherbereich wird nicht gelöscht.
Bei einem Fehler gibt malloc() NULL zurück.
Dynamisch angeforderter Speicher muss wieder freigegeben werden. Hierzu dient die Funktion
void free(void *ptr);
Sie erhält als Argument einen Zeiger auf den Anfang eines dynamisch angeforderten Speicherbereiches, wie er von calloc() oder malloc() als Ergebnis zurückgeliefert wurde.
Das Programm ex083 berechnet den Mittelwert mehrerer Gleitkommazahlen. Beim Programmstart wird gefragt, wieviele Werte eingegeben werden sollen. Diese Anzahl muss positiv sein.
In der Funktion durchschnitt_mehrerer_werte() wird
zunächst geprüft, ob sich bei der Multiplikation der Werteanzahl
mit der Größe des Datentyps double ein Überlauf im
size_t-Datentyp ergibt.
Falls dies nicht der Fall ist, versucht das Programm, dynamisch
Speicher anzufordern. Hierzu wird die Funktion calloc()
benutzt, diese erhält als Argumente die Anzahl der Werte (die vom
Nutzer eingegebene Zahl) und die Größe eines einzelnen Wertes (da
alle Werte vom Datentyp double sind, wird hier
sizeof(double) benutzt).
Der Zeiger buffer wird auf die erhaltene Adresse
gesetzt.
Die Speicheranforderung kann fehlschlagen (z.B. wenn der gesamte
Speicher bereits benutzt wird), daher muss getestet werden, ob der
erhaltene Zeiger NULL ist. Nur wenn der Zeiger ungleich NULL ist,
können wir den Speicher benutzen. Dynamisch angeforderter Speicher
muss nach der Benutzung mit free() wieder freigegeben
werden.
Die Funktion werte_einlesen() liest die Werte in den Puffer
ein. Ungültige Eingaben oder Leerzeilen-Eingaben führen zum
Programmabbruch.
Die Funktion mittelwert_berechnen() berechnet den Mittelwert
und gibt ihn aus.
/** @file ex083.c Demonstation von calloc() und free().
Das Programm erhaelt keine Argumente uebergeben.
*/
#include "he-conf.h"
#include <stdio.h>
#include <stdlib.h>
#if HAVE_LIMITS_H
#include <limits.h>
#endif
#if HAVE_STDINT_H
#include <stdint.h>
#endif
#include "he-read.h"
/** Status-Code des Programmes, wird von main-Funktion zurueckgegeben.
*/
static int excode = EXIT_FAILURE;
/** Gleitkommazahlen in Puffer einlesen.
@param buffer Puffer, der mit Werten gefuellt werden soll.
@param anz Groesse des Puffers (Anzahl der Werte).
@return 1 bei Erfolg, 0 bei Fehler, z.B. Abbruch oder ungueltige Eingabe.
*/
static
int
werte_einlesen(double *buffer, size_t anz)
{
int back = 1; /* Ergebnis der Funktion */
size_t i; /* Index aktuell bearbeiteter Wert */
for (i = 0; ((i < anz) && (back == 1)); i++) {
if (0 == hsm_et_read_double("Wert: ", buffer++)) {
back = 0;
}
}
return back;
}
/** Mittelwert mehrerer Werte berechnen.
@param buffer Adresse des Puffers, in dem die Werte stehen.
@param anz Anzahl der Werte im Puffer, muss positiv sein.
*/
static
void
mittelwert_berechnen(double *buffer, size_t anz)
{
double summe = 0.0; /* Summe der Elemente */
size_t i; /* Index aktuell bearbeitetes Element */
for (i = 0; i < anz; i++) {
summe += *(buffer++);
}
summe = summe / (double)anz;
printf("Mittelwert: %g\n", summe);
}
/** Puffer fuer mehrere Werte dynamisch anfordern, Werte einlesen,
Mittelwert berechnen und ausgeben.
@param anz Anzahl der Werte, muss positiv sein.
*/
static
void
durchschnitt_mehrerer_werte(int anz)
{
double *buffer = NULL; /* Dynamisch allozierter Puffer fuer Werte */
size_t sz_anz; /* Anzahl Werte im Datentyp size_t */
sz_anz = (size_t)anz;
if ((SIZE_MAX / sizeof(double)) >= sz_anz) {
buffer = calloc(sz_anz, sizeof(double));
if (NULL != buffer) {
/*
Der von calloc() erhaltene Zeiger ist ungleich NULL,
wir koennen den Speicher verwenden.
*/
if (0 != werte_einlesen(buffer, sz_anz)) {
mittelwert_berechnen(buffer, sz_anz);
excode = EXIT_SUCCESS;
}
/* Speicher nach Benutzung wieder freigeben
*/
free(buffer);
}
else {
/*
Der von calloc() erhaltene Zeiger ist NULL,
die Speicheranforderung ist fehlgeschlagen!
*/
printf("FEHLER: Speicher nicht erhalten!\n");
}
}
else {
printf("FEHLER: Numerischer Ueberlauf, die Anzahl ist zu gross!\n");
}
}
/** Hauptprogramm.
@return EXIT_SUCCESS (0) bei Erfolg, EXIT_FAILURE (ungleich 0) bei Fehler.
*/
int
main(void)
{
int anzahl = 0; /* Anzahl der Werte */
printf("Mittelwertberechnung\n");
if (0 != hsm_et_read_int("Anzahl der Werte: ", &anzahl)) {
if (0 < anzahl) {
durchschnitt_mehrerer_werte(anzahl);
}
else {
printf("FEHLER: Die Anzahl muss positiv sein!\n");
}
}
return excode;
}
/* vim: set ai sw=4 ts=4 : */
Quelle: CERT C Coding Standard 〈1〉
int *dynint = NULL; ... dynint = (int *)calloc( n, sizeof(int) ); if (NULL != dynint) { /* ... Verwendung von dynint ... */ free(dynint); dynint = NULL; } else { /* !!! FEHLERMELDUNG, SPEICHERANFORDERUNG FEHLGESCHLAGEN !!! */ }
int *dynint = NULL; ... if (0 != n) { dynint = (int *)calloc( n, sizeof(int) ); if (NULL != dynint) { /* ... Verwendung von dynint ... */ free(dynint); dynint = NULL; } else { /* !!! FEHLERMELDUNG, SPEICHERANFORDERUNG FEHLGESCHLAGEN !!! */ } } else { /* !!! FEHLERMELDUNG, n IST 0 !!! */ }
#include <stdint.h> ... int *dynint = NULL; ... if (0 != n) { if(((uintmax_t)SIZE_MAX / (uintmax_t)sizeof(int)) >= (uintmax_t)n) { dynint = (int *)calloc( n, sizeof(int) ); if (NULL != dynint) { /* ... Verwendung von dynint ... */ free(dynint); dynint = NULL; } else { /* !!! FEHLERMELDUNG, SPEICHERANFORDERUNG FEHLGESCHLAGEN !!! */ } } else { /* !!! FEHLERMELDUNG, UEBERLAUF IN GROESSENBERECHNUNG !!! */ } } else { /* !!! FEHLERMELDUNG, n IST 0 !!! */ }
#include <stdint.h> ... int *dynint = NULL; ... if (0 != n) { if(((uintmax_t)SIZE_MAX / (uintmax_t)sizeof(int)) >= (uintmax_t)n) { dynint = (int *)calloc( n, sizeof(int) ); if (NULL != dynint) { /* ... Verwendung von dynint ... */ free(dynint); dynint = NULL; } else { /* !!! FEHLERMELDUNG, SPEICHERANFORDERUNG FEHLGESCHLAGEN !!! */ } } else { /* !!! FEHLERMELDUNG, UEBERLAUF IN GROESSENBERECHNUNG !!! */ } } else { /* !!! FEHLERMELDUNG, n IST 0 !!! */ }mit Test der Größen auf 0, Test auf Überlauf, Speicheranforderung, Prüfung und Freigabe schreiben, anschließend den Code, der den angeforderten Speicher verwendet.
1 | http://www.securecoding.cert.org |