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

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 12 Präprozessor-Direktiven
  gp 12.1 Einkopieren von Dateien mittels #include
  gp 12.2 Makros und Konstanten – #define
    gp 12.2.1 Symbolische Konstanten mit #define
    gp 12.2.2 Makros mit #define
  gp 12.3 Bedingte Kompilierung
  gp 12.4 Vordefinierte Präprozessor-Direktiven (ANSI C)
  gp 12.5 Ersetzung eines Makroparameters durch einen String
  gp 12.6 #undef – Makronamen wieder aufheben
  gp 12.7 Ausgeben von Fehlermeldungen – #error
  gp 12.8 #pragma


Galileo Computing - Zum Seitenanfang

12.2 Makros und Konstanten – #define  downtop

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. Hierzu die Syntax der define-Direktive:

#define Bezeichner    Ersatzbezeichner
#define Bezeichner(Bezeichner_Liste)    Ersatzbezeichner

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


Galileo Computing - Zum Seitenanfang

12.2.1 Symbolische Konstanten mit #define  downtop

Ein erstes Programmbeispiel, welches 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 der Compilerflag -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 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 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"

oder auf MS Windows-Systemen (C:\ sei Ihr Arbeitslaufwerk):

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

Galileo Computing - Zum Seitenanfang

12.2.2 Makros mit #define  toptop

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)

Allein stehende 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 muss beachtet werden, 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, welche 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 kennen lernen 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.


 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2006
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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de