Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 10 Präprozessor-Direktiven
Pfeil 10.1 Einkopieren von Dateien mittels »#include«
Pfeil 10.2 Makros und Konstanten – »#define«
Pfeil 10.2.1 Symbolische Konstanten mit »#define«
Pfeil 10.2.2 Makros mit »#define«
Pfeil 10.3 Bedingte Kompilierung
Pfeil 10.4 Vordefinierte Präprozessor-Direktiven (ANSI C)
Pfeil 10.5 Ersetzung eines Makroparameters durch einen String
Pfeil 10.6 »#undef« – Makronamen wieder aufheben
Pfeil 10.7 Ausgeben von Fehlermeldungen – »#error«
Pfeil 10.8 »#pragma«


Rheinwerk Computing - Zum Seitenanfang

10.2 Makros und Konstanten – »#define« Zur nächsten ÜberschriftZur vorigen Überschrift

Mit #define ist es möglich, Zeichenketten anzugeben, die vor der Übersetzung des Programms gegen eine andere Zeichenkette ausgetauscht werden. Sie erinnern sich sicherlich aus dem vorangegangenen Kapitel daran, wie ein Programm übersetzt wird. Auch hier wird durch das Zeichen # bewirkt, dass der Präprozessor zuerst seine Arbeit verrichtet, bevor das werdende Programm vom Compiler in Assembler und dann in Maschinensprache übersetzt wird. Die Syntax der define-Direktive sieht so aus:

#define Bezeichner    Ersatzbezeichner
#define Bezeichner(Bezeichner_Liste)    Ersatzbezeichner

Bei der ersten Syntaxbeschreibung wird eine symbolische Konstante und im zweiten Fall ein Makro definiert.


Rheinwerk Computing - Zum Seitenanfang

10.2.1 Symbolische Konstanten mit »#define« Zur nächsten ÜberschriftZur vorigen Überschrift

Hier sehen Sie ein erstes Programmbeispiel, das eine symbolische Konstante definiert:

/* define1.c */
#include <stdio.h>
#include <stdlib.h>
#define EINS 1

int main(void) {
   printf("%d\n",EINS);
   return EXIT_SUCCESS;
}

Im Programm wird jede symbolische Konstante EINS mit dem Wert 1 definiert. Wenn Sie das Programm übersetzen, werden vor der Kompilierung alle Namen mit EINS im Quelltext vom Präprozessor durch den Wert 1 ersetzt. Die Konstante EINS müssen Sie nicht wie im Beispiel ausdrücklich in großen Buchstaben schreiben. Dies dient nur der besseren Übersicht. Aber Achtung, Folgendes funktioniert nicht:

printf("EINS");

In diesem Fall wird tatsächlich der String "EINS" auf dem Bildschirm ausgegeben und nicht der Wert 1. Das bedeutet, hier wird die Konstante EINS nicht durch 1 ersetzt.


Merke

Beachten Sie, dass #define-Makros Konstanten sind. Einmal festgelegte Konstanten können zur Laufzeit des Programms nicht mehr geändert werden.


Welchen Vorteil haben solche Defines? Das soll das folgende Programm demonstrieren:

/* kreisber.c */
#include <stdio.h>
#include <stdlib.h>
/*  Bei Linux muss für math.h das Compiler-Flag -lm
 *  mit angegeben werden:
 *  gcc -o synkonst2 symkonst2.c -lm
 */
#include <math.h>
#define PI 3.1415926f

/*  Programm zur Berechnung von Kreisfläche(A), Durchmesser(d)
 *  und Umfang(U) und Durchmesser aus Umfang */

void kreisflaeche(void) {
   float A,d;

   printf("Durchmesser des Kreises eingeben: ");
   scanf("%f", &d);
   A = d*d*PI / 4;
   printf("Die Kreisfläche beträgt  %f\n", A);
}

void durchmesser(void) {
   float A, d;

   printf("Kreisfläche des Kreises eingeben: ");
   scanf("%f", &A);
   d =(float) sqrt((double)4*A/PI);
   printf("Der Duchmesser des Kreises ist %f\n", d);
}

void kreisumfang(void) {
   float U, d;

   printf("Durchmesser des Kreises eingeben: ");
   scanf("%f", &d);
   U = d * PI;
   printf("Der Umfang des Kreises beträgt %f\n", U);
}

void d2umfang(void) {
   float U,d;

   printf("Umfang des Kreises eingeben: ");
   scanf("%f",&U);
   d = U/PI;
   printf("Der Durchmesser des Kreises beträgt %f\n", d);
}

int main(void) {
   kreisflaeche();
   durchmesser();
   kreisumfang();
   d2umfang();
   return EXIT_SUCCESS;
}

In diesem Programm werden einfache Berechnungen von kreisförmigen Flächen durchgeführt. Statt PI an jeder Stelle im Programm erneut festzulegen, ist hier die textliche Ersetzung mittels define besser geeignet. Dadurch wird auch garantiert, dass stets der gleiche Wert überall im Programm verwendet wird. Sollten Sie z. B. eine genauere Angabe von PI benötigen, so müssen Sie nur die symbolische Konstante ändern.

Einen weiteren Vorteil bietet z. B. die Verwendung bestimmter Konstanten, etwa einer Landeswährung. Falls eine Änderung erforderlich wird, kann diese ohne viel Aufwand für das gesamte Programm an einer zentralen Stelle vorgenommen werden. Sie können bei Makrodefinitionen auch auf früher definierte Namen zurückgreifen, wie im folgenden Beispiel:

#define PI 3.141592653
#define PI_2 PI*2

Hier wird zuerst PI definiert und in der nächsten Zeile der Wert von PI*2, der textlich durch PI_2 ersetzt wird.


Tipp

Verzichten Sie bei textlichen Ersetzungen auf überflüssige Berechnungen. So führt zum Beispiel ein Define der Art #define PI atan(1)*4 dazu, dass dieser Wert im Programm jedes Mal erneut berechnet wird. Verwenden Sie für solche Fälle besser eine const-Variable wie zum Beispiel:

const double PI = atan(1)*4;


Mit der #define-Direktive können nicht nur Zahlen als symbolische Konstanten festgelegt werden, sondern auch Strings. Beispiel:

#include <stdio.h>
#define GANZZAHL     int
#define SCHREIB      printf(
#define END          );
#define EINGABE      scanf(
#define ENDESTART    return 0;
#define NEUEZEILE    printf("\n");
#define START        int main()

#define BLOCKANFANG  {
#define BLOCKENDE    }

Mit diesen Festlegungen wurde mit minimalem Aufwand eine eigene kleine Programmiersprache erzeugt! Ein Programm in der neuen Sprache könnte zum Beispiel so aussehen:

START
 BLOCKANFANG
   GANZZAHL zahl;
   SCHREIB "Hallo Welt" END
   NEUEZEILE
   SCHREIB "Zahleingabe: " END
   EINGABE "%d", &zahl END
   SCHREIB "Die Zahl war %d", zahl END
 ENDESTART
BLOCKENDE

Hier wurde nicht wirklich eine neue Programmiersprache erzeugt. Statt int main() wird in dem Programm einfach START geschrieben, oder statt return 0 wird ENDESTART geschrieben. Der Präprozessor ersetzt vor der Übersetzung des Compilers die Pseudo-Sprache wieder nach C.

Diese Pseudo-Sprache soll jetzt in eine eigene Headerdatei gepackt werden. Legen Sie dazu eine neue Quelldatei mit folgendem Inhalt an:

/* mysyntax.h */
#ifndef MYSYNTAX_H
#define MYSYNTAX_H

#include <stdio.h>
#include <stdlib.h>
#define GANZZAHL         int
#define SCHREIB          printf(
#define END              );
#define EINGABE          scanf(
#define ENDESTART        return EXIT_SUCCESS;
#define NEUEZEILE        printf("\n");
#define START            int main()
#define BLOCKANFANG      {

#define BLOCKENDE        }

#endif /*MYSYNTAX_H*/

Speichern Sie diese Codezeilen unter dem Namen MYSYNTAX.H. Jetzt folgt noch das Hauptprogramm inklusive der neuen Headerdatei:

/* mein_C.c */
#include "mysyntax.h"

START
 BLOCKANFANG
     GANZZAHL zahl;
     SCHREIB "Hallo Welt" END
     NEUEZEILE
     SCHREIB "Zahleingabe: " END
     EINGABE "%d", &zahl END

     SCHREIB "Die Zahl war %d", zahl END
     NEUEZEILE
   ENDESTART
 BLOCKENDE

Speichern Sie das Hauptprogramm im selben Verzeichnis, in dem sich auch mysyntax.h befindet. Den Namen für das Hauptprogramm können Sie frei wählen, zum Beispiel: mein_C.c. Übersetzen Sie dieses Programm. Befindet sich die Headerdatei mysyntax.h in einem anderen Verzeichnis als das Hauptprogramm, muss dies dem Compiler mitgeteilt werden. Befindet sich die Headerdatei z. B. in /home/myhome/myheader, wird dies dem Präprozessor wie folgt mitgeteilt:

#include "/home/myhome/myheader/mysyntax.h"

Auf MS Windows-Systemen muss das so aussehen (C:\ sei Ihr Arbeitslaufwerk):

#include "c:\Programme\mysyntax.h"

Rheinwerk Computing - Zum Seitenanfang

10.2.2 Makros mit »#define« topZur vorigen Überschrift

Weiterhin haben Sie die Möglichkeit, mit der define-Direktive parametrisierte Makros zu schreiben. Ein Beispiel:

/* define2.c */
#include <stdio.h>
#include <stdlib.h>
#define KLEINER_100(x) ((x) < 100)


void klHundert(int zahl) {
   if(KLEINER_100(zahl))
      printf("Ja! Die Zahl ist kleiner als 100!\n");
   else
      printf("Die Zahl ist größer als 100!\n");
}

int main(void) {
   int b = 99;

   klHundert(b);
   return EXIT_SUCCESS;
}

Ein parametrisiertes Makro erkennen Sie daran, dass unmittelbar nach dem Makronamen eine Klammer folgt:

#define KLEINER_100(x)  ((x) < 100)

Alleinstehende Makros benötigen bei Verwendung im Programm kein Semikolon am Ende der Zeile. Daran lassen sich Makros auch oft erkennen. Es wird zwar nicht vom Compiler moniert, wenn Sie dennoch Semikolons setzen; es ist aber nicht erforderlich.

Im betrachteten Fall haben Sie den formalen Parameter x. Dieser kann auf der rechten Seite des Makros beliebig oft verwendet werden. Dabei müssen Sie beachten, dass dieser formale Parameter ebenfalls auf der rechten Seite in Klammern stehen muss. Folgende Definition wäre falsch:

#define KLEINER_100(x)  (x < 100)

da sich hier der Parameter x nicht zwischen Klammern befindet. Die Zeile

if(KLEINER_100(zahl))

sieht nach dem Präprozessorlauf, also vor der eigentlichen Kompilierung, so aus:

if((zahl) < 100)

Eine weitere, häufig eingesetzte Variante dieser Art ist:

#define MAX(x,y) ( (x)<=(y) ?(y) :(x) )

Hier werden gleich zwei Argumente als Parameter verwendet. Beide Parameter werden durch ein Komma voneinander getrennt. Bei diesem Makro wird die größere der beiden Dezimalzahlen ermittelt. Ein weiteres Beispiel:

#define TAUSCHE(x,y)   { \
   int j; \
   j=x; x=y; y=j; \
  }

Mit diesem Makro werden zwei Integer-Werte vertauscht. Wie sich ein Makro mit mehreren Statements über mehrere Zeilen erstrecken kann, lässt sich an diesem Beispiel ebenfalls erkennen. Bei der Makrodefinition muss an jedem Zeilenende ein Backslash geschrieben werden.

Lange Makros, auf die häufig zugegriffen wird, können allerdings den Code unnötig aufblähen. In solch einem Fall sind Funktionen besser geeignet. Hierzu ein Negativ-Beispiel:

/* bad_define1.c */
#include <stdio.h>
#include <stdlib.h>
#define VIEL_TEXT "TextTextTextTextTextTextTextTextTextText"\
                  "TextTextTextTextTextTextTextTextTextText"\
                  "TextTextTextTextTextTextTextTextTextText"\
                  "TextTextTextTextTextTextTextTextTextText\n"

int main(void) {
   printf(VIEL_TEXT);
   printf(VIEL_TEXT);
   printf(VIEL_TEXT);
   return EXIT_SUCCESS;
}

Dieses Programm würde nach dem Präprozessorlauf und vor dem Compilerlauf folgendermaßen aussehen:

/* bad_define2.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   printf("TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText\n");
   printf("TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText\n");
   printf("TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText\n");
   return EXIT_SUCCESS;
}

Jetzt dasselbe Beispiel mit einer Funktion, die in diesem Fall die effizientere Methode darstellt:

/* without_define.c */
#include <stdio.h>
#include <stdlib.h>

void viel_text(void) {
   printf("TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText"\
          "TextTextTextTextTextTextTextTextTextText\n");
}

int main(void) {
   viel_text();
   viel_text();
   viel_text();
   return EXIT_SUCCESS;
}

Die define-Direktive ist im Übrigen eine rein für die Programmiersprache C gedachte Direktive. Ein reiner C++-Compiler wird define deshalb nicht erkennen und kompilieren. Die meisten Compiler kennen aber sowohl C als auch C++.

In der Regel sollten hier also keine Probleme beim Kompilieren auftreten. Dies nur ergänzend zum Thema, falls Sie die Grundlagen in C kennenlernen wollen, um anschließend mit C++ fortzufahren. Unter C++ und dem neuen ANSI-C99-Standard können kleinere Funktionsmakros außerdem durch inline-Funktionen ersetzt werden.


Hinweis

Der Geltungsbereich von symbolischen Konstanten bzw. Makros reicht vom Punkt der Deklaration mit #define bis zur Aufhebung mit #undef. Die Aufhebung mittels #undef ist aber optional. Wird #undef nicht verwendet, reicht der Geltungsbereich bis zum Dateiende.




Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z

 C von A bis Z
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: C/C++






 C/C++


Zum Katalog: Einstieg in C






 Einstieg in C


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de