Beispiel zu einem kleinen Projekt

Das folgende Beispiel demonstriert ein kleines Projekt, in dem der Quellcode auf zwei Dateien mit einer gemeinsamen Header-Datei verteilt wird. Es werden zwei Funktionen zur sicheren Benutzerabfrage von Zahlen erstellt. Im Folgenden werden die drei Dateien aufgelistet gefolgt von einigen Erläuterungen. Hinter allen unterstrichenen Passagen verbirgt sich weiter unten eine kurze Erläuterung; einfach anklicken.


Die Headerdatei get.h

/**********************************************************************/
/* Two functions that read double- and int-values from user.          */
/* This is to demonstrate a mini project.                             */
/* author: Robert Hess                                                */
/* version 1.11                                                       */
/* date: December 22nd 2007                                           */
/* last modified: February 20th 2020                                  */
/* filename: get.h                                                    */
/**********************************************************************/

#ifndef FUNCTIONS_GET_FROM_USER_BY_ROBERT_HESS_INCLUDED
#define FUNCTIONS_GET_FROM_USER_BY_ROBERT_HESS_INCLUDED

double getDouble(char *text, double min, double max, double oldValue);
int getInt(char *text, int min, int max, int oldValue);

#endif /*#ifndef FUNCTIONS_GET_FROM_USER_BY_ROBERT_HESS_INCLUDED*/

Die Quellcodedatei get.c

/**********************************************************************/
/* Two functions that read double- and int-values from user.          */
/* This is to demonstrate a mini project.                             */
/* author: Robert Hess                                                */
/* version 1.11                                                       */
/* date: December 22nd 2007                                           */
/* last modified: February 2020                                       */
/* filename: get.c                                                    */
/**********************************************************************/

#include "get.h"
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

/**********************************************************************/

/* Reads a double value from the user. */
double getDouble(
    char *text,         /* question text */
    double min,         /* minimum value */
    double max,         /* maximum value */
    double oldValue)    /* previous value */
{
    char line[50];      /* user input line */
    double newValue=0;  /* new user input value */
    int finished=0;     /* flag for correct input */
    char ch;            /* character behind number */

    do {
        /* print user request */
        printf("%s (%g - %g, alter Wert: %g): ", text, min, max, oldValue);

        /* get user input */
        fgets(line, 50, stdin);

        /* if user pressed ENTER only use old value */
        if(*line=='\n') {
            newValue = oldValue;
            finished = 1;

        /* check if user typed a number */
        } else if(sscanf(line, "%lf%c", &newValue, &ch)!=2 || ch!='\n')
            printf("Das war keine gueltige Zahl!\n");

        /* check range */
        else if(newValue<min || newValue>max)
            printf("Die Zahl liegt ausserhalb des gueltigen Bereichs!\n");

        /* number is correct */
        else finished = 1;

    /* repeat until finished */
    } while(!finished);

    /* return new value */
    return newValue;
}

/**********************************************************************/

/* Reads an int value from the user. */
int getInt(
    char *text,     /* question text */
    int min,        /* minimum value */
    int max,        /* maxumum value */
    int oldValue)   /* previous value */
{
    char line[50];      /* user input line */
    int newValue=0;     /* new user input value */
    int finished=0;     /* flag for correct input */
    char ch;            /* character behind number */

    do {
        /* print user request */
        printf("%s (%d - %d, alter Wert: %d): ", text, min, max, oldValue);

        /* get user input */
        fgets(line, 50, stdin);

        /* if user pressed ENTER only use old value */
        if(*line=='\n') {
            newValue = oldValue;
            finished = 1;

        /* check if user typed a number */
        } else if(sscanf(line, "%d%c", &newValue, &ch)!=2 || ch!='\n')
            printf("Das war keine gueltige Zahl!\n");

        /* check range */
        else if(newValue<min || newValue>max)
            printf("Die Zahl liegt ausserhalb des gueltigen Bereichs!\n");

        /* number is correct */
        else finished = 1;

    /* repeat until finished */
    } while(!finished);

    /* return new value */
    return newValue;
}

Die Quellcodedatei main.c

/**********************************************************************/
/* Two functions that read double- and int-values from user.          */
/* This is to demonstrate a mini project.                             */
/* author: Robert Hess                                                */
/* version 1.11                                                       */
/* date: December 22nd 2007                                           */
/* last modified: February 20th 2020                                  */
/* filename: main.c                                                   */
/**********************************************************************/

#include "get.h"
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    double d=3.141; /* a double-value */
    int i=8;        /* an integer-value */

    /* get a double-value from user */
    d = getDouble("Bitte geben Sie eine Zahl ein", 1.2, 4.5, d);
    printf("Sie haben folgenden Wert eingegeben: %g\n\n", d);

    /* get a int-value from user */
    i = getInt("Bitte geben Sie eine Zahl ein", 5, 10, i);
    printf("Sie haben folgenden Wert eingegeben: %d\n\n", i);

    return 0;
}

Verwendung unter Microsoft Visual Studio 2017

Mit den folgenden Schritten können Sie das Mini-Projekt unter Microsoft Visual Studio 2017 zum Laufen bringen. Es wird von einer englischen Installation ausgegangen; bei andersprachigen Installationen müssen die Befehle sinngemäß übersetzt werden.

  1. Starten Sie Visual Studio 2017
  2. Estellen Sie mit File->New->Project... ein Empty Priject, geben Sie ihr einen sinnvollen Namen, wählen Sie den Speicherort aus, entfernen sie die Haken bei Create directory for solution und Add to Source Control und klicken Sie Ok.
  3. Wählen Sie im Menü Project->Add New Item..., wählen Sie dann Header File (.h), vergeben Sie den Dateinamen get.h und klicken Sie auf Add.
  4. Löschen Sie die Zeile #pragma once.
  5. Kopieren Sie in die gerade erstellte Datei den weiter oben auf dieser Seite gezeigten Inhalt für get.h.
  6. Wählen Sie im Menü Project->Add New Item..., wählen Sie dann C++ File (.cpp), vergeben Sie den Dateinamen get.c und klicken Sie auf Add. (Hinweis: Durch die Dateiendung .c wird dem Compiler mitgeteilt, dass es sich um eine C- und nicht um eine C++-Datei handelt.)
  7. Kopieren Sie in die gerade erstellte Datei den weiter oben auf dieser Seite gezeigten Inhalt für get.c.
  8. Wählen Sie im Menü Project->Add New Item..., wählen Sie dann C++ File (.cpp), vergeben Sie den Dateinamen main.c und klicken Sie auf Add.
  9. Kopieren Sie in die gerade erstellte Datei den weiter oben auf dieser Seite gezeigten Inhalt für main.c.
  10. Jetzt kann das Projekt übersetzt und gestartet werden.
Hinweis: Wenn das Programm in Visual Studio mit Strg+F5 oder die exe-Datei direkt ausgeführt wird, verschwindet das Fenster direkt nach Beendigung des Programms. Um das zu verhindern müssen in main.c zwei Zeilen eingefügt werden:
  1. Fügen Sie oben die Header-Datei stdlib.h ein.
  2. Fügen Sie unten vor return 0; die Zeile system("pause"); ein.

Zwei Quellcode-Dateien

In dem hier gezeigten Beispiel ist der Quellcode auf zwei Dateien verteilt. Die Datei get.c beinhaltet die beiden Funktionen getDouble() und getInt(). In der Datei main.c ist nur das Hauptprogramm main() implementiert, welche die beiden anderen Funktionen verwendet.

Die Header-Datei

Die Header-Datei get.h stellt das Bindeglied zwischen den beiden Quellcode-Dateien dar. In ihr stehen die Deklarationen (Ankündigungen) der Funktionen getDouble() und getInt(). Die Header-Datei wird in alle Quellcode-Dateien eingebunden, welche getDouble() und getInt() verwenden.

Einbinden der Header-Datei

Die Header-Datei wird mit dem Präprozessorbefehl #include eingebunden. Der Präprozessor entfernt die Zeile selbst, und fügt dafür den gesammten Inhalt der Header-Datei ein.

Neu ist, dass statt der spitzen Klammern <...> hier die Anführungszeichen "..." verwendet werden. Das hat folgenden Grund: Bei Dateinamen in spitzen Klammern sucht der Compiler nur in seinen Standardverzeichnissen nach dieser Datei. Steht der Dateiname in Anführungszeichen ("Gänsefüßchen"), so sucht der Compiler zuerst im aktuellen Verzeichnis nach der angegebenen Datei, bevor er dann in seinen Standardverzeichnissen weitersucht. Zusätzlich wird durch die Anführungszeichen dem Leser des Programms deutlich gemacht, welche Header-Dateien speziell für das Projekt erstellt wurden.

Header-Datei nur einmal übersetzen

In Projekten zunehmender Größe kann es vorkommen, dass eine Header-Datei innerhalb einer Quelcode-Datei mehrfach eingebunden wird. Entweder wurde die Header-Datei tatsächlich durch zwei identische #include-Befehle eingebunden, oder es wurden zwei unterschiedliche Header-Dateien eingebunden, von denen die eine die andere wiederum mit #include einbindet. Wird eine Header-Datei mehrfach eingebunden, so kommt es zu Mehrfachdeklarationen und -definitionen und der Compiler wird Fehler melden.

Der Ausweg ist eine bedingte Übersetzung mit #ifndef. Mit der ersten Zeile wird geprüft, ob ein recht langer Name (hier: FUNCTIONS_GET_FROM_USER_BY_ROBERT_HESS_INCLUDED) noch nicht definiert worden ist (if not defined). Nur wenn der Name noch nicht definiert wurde, wird der block zwischen #ifndef und #endif übersetzt. Damit dieser Block aber nur einmal übersetzt wird, sorgt der #define-Befehl dafür, das der lange Name jetzt definiert wird. Dabei muss dem Namen kein Inhalt zugewiesen werden (wie z.B. #define ANZAHL 10), es reicht, dass der Name existiert.

Wird die Header-Datei erneut eingebunden, so existiert der lange Name bereits, und der Block zwischen #ifndef und #endif wird nicht wieder übersetzt.

























Seite 22