Inhaltsverzeichnis

Vordefinierte Präprozessor-Direktiven (ANSI C)

Bedingtes Kompilieren

Sie binden die angegebene Datei in die aktuelle Source-Datei ein. Es gibt zwei Arten der #include-Direktive, nämlich:

#include <Datei.h>

und

#include "Datei.h"

Die erste Anweisung sucht die Datei im Standard-Includeverzeichnis des Compilers, die zweite Anweisung sucht die Datei zuerst im Verzeichnis, in der sich die aktuelle Sourcedatei befindet; sollte dort keine Datei mit diesem Namen vorhanden sein, sucht sie ebenfalls im Standard-Includeverzeichnis.

#define SYMBOL
#define KONSTANTE (Wert)
#define MAKRO Ausdruck
#undef SYMBOL
#ifdef
#ifndef
#elif
#else
#endif

Die #error-Direktive wird verwendet, um den Kompilierungsvorgang mit einer (optionalen) Fehlermeldung abzubrechen. Syntax:

#error Fehlermeldung

Die Fehlermeldung muss nicht in Anführungszeichen stehen.

Mit #if kann ähnlich wie mit #ifdef eine bedingte Übersetzung eingeleitet werden, jedoch können hier konstante Ausdrücke ausgewertet werden.

#if (DEBUGLEVEL >= 1)
#  define print1 printf
#else
#  define print1(...) (0)
#endif
 
#if (DEBUGLEVEL >= 2)
#  define print2 printf
#else
#  define print2(...) (0)
#endif

Der Präprozessorausdruck innerhalb der Bedingung folgt den gleichen Regeln wie Ausdrücke in C, jedoch muss das Ergebnis zum Übersetzungszeitpunkt bekannt sein.

defined ist ein unärer Operator, der in den Ausdrücken der #if und #elif Direktiven eingesetzt werden kann.

#define FOO
#if defined FOO || defined BAR
#error "FOO oder BAR ist definiert"
#endif

Bei den #pragma-Anweisungen handelt es sich um compilerspezifische Erweiterungen der Sprache C. Diese Anweisungen steuern meist die Codegenerierung. Sie sind aber zu sehr von den Möglichkeiten des jeweiligen Compilers abhängig, als dass man hierzu eine allgemeine Aussage treffen kann.

Quelle: wikibooks

Vordefinierte Makros (Predefined Macros)

ANSI-kompatible Makros:

(unvollständig)

Makroname Bedeutung
__LINE__ Zeilennummer der aktuellen Zeile in der Programmdatei (integer)
__FILE__ Name der Programmdatei (String)
__DATE__ Übersetzungsdatum der Programmdatei (String: Mmm dd yyyy)
__TIME__ Übersetzungszeit der Programmdatei (String: hh:mm:ss)
__STDC__ Erkennungsmerkmal eines ANSI C-Compilers. Ist die ganz-zahlige Konstante auf den Wert 1 gesetzt, handelt es sich um einen ANSI C-konformen Compiler.
__func__ Gibt den Namen der Funktion aus, in der dieses Makro verwendet wird.*
__TIMESTAMP__ "mtime" von dem source-File (String: Ddd Mmm Date hh:mm:ss yyyy)

GCC: Predefined Macros

Pre-defined Compiler Macros: https://sourceforge.net/p/predef/wiki/Home/

Microsoft-spezifische Makros:

(unvollständig)

Makroname Bedeutung
__cplusplus C++-Code
__FUNCTION__ Gibt den Namen der Funktion aus, in der dieses Makro verwendet wird.*
__FUNCDNAME__ Gibt Decorated function name aus, z.B.: ?exampleFunction@@YAXXZ
__FUNCSIG__ Gibt Function signature aus, z.B.: void __cdecl exampleFunction(void)
_MFC_VER Gibt die Version von MFC aus. Zum Beispiel, 0x0700 entspricht MFC Version 7.
Hier ist die Auflistung, welche Werte welcher Version des MSVC-Compilers entsprechen:
https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering
_WIN32 Definiert für Win32- und Win64-Anwendungen (immer definiert)
_WIN64 Definiert für Win64-Anwendungen
_M_AMD64 Definiert für Kompilierungen, die auf x64-Prozessoren abzielen.
_M_ARM Definiert für Kompilierungen, die auf ARM-Prozessoren abzielen.
_M_IX86 Definiert für Kompilierungen, die auf x86-Prozessoren abzielen.
_M_X64 Definiert für Kompilierungen, die auf x64-Prozessoren abzielen.

:!: Das Makro __func__ funktioniert nicht mit allen (pre-)Compiler. Der Compiler von MS VisualStudio 2010 z.B. meldet dieses Makro als undeclared identifier. Stattdessen soll das Makro __FUNCTION__ verwendet werden. (Hier gehts zu den MS VisualStudio 2010 Predefined Macros, oder Predefined Macros in VisualStudio 2013 deutsch bzw. Originaltext. Weitere Infos unter MACRO Revisited.)
Um übergreifenden Code zu schreiben kann man es beispielsweise mit folgender Definition umgehen:

#ifndef __func__
#define __func__ __FUNCTION__
#endif

:!: Oder alternativ, damit die Makros einheitlich aussehen, ein übergreifendes Makro namens __FUNC__ definieren, wie folgt:

#if defined _WIN32 || defined _WIN64
#define __FUNC__	__FUNCTION__
#else
#define __FUNC__	__func__
#endif
 
#if !defined __FUNC__
#error Define for __FUNC__ expected
#endif

Hier folgt ein Beispiel zur Verwendung dieser vordefinierten Makros:

/* direktiven.c */
#include <stdio.h>
#include <stdlib.h>
 
#if defined __STDC__
  #define isstd_c() printf("ANSI C Compiler\n")
#else
  #define isstd_c() printf("Kein ANSI C Compiler\n")
#endif
 
int main(void)
{
  printf("Zeile %d in Datei %s\n",__LINE__,__FILE__);
  printf("Übersetzt am %s um %s\n",__DATE__,__TIME__);
 
  #line 999 "asdf.c"
  printf("Zeile %d in Datei %s\n",__LINE__,__FILE__);
 
  isstd_c();  /* Ist es ein ANSI C Compiler ? */
  return EXIT_SUCCESS;
}

Ein Beispiel für platformabhängiges Kompilieren:

#if defined _WIN64
// Hier kommt Code für Windows 64 Bit (muß unbedingt vor dem "_WIN32"-Zweig stehen!)
// ... Text ...
#elif defined _WIN32
// Hier kommt Code für Windows 32 Bit
// ... Text ...
#else
// Restlicher Code
// ... Text ...
#endif

Siehe auch: Plattform-Makros

String-Literal-Operator

:!: Zitat (Quelle: http://wwwuser.gwdg.de/~kboehm/ebook/26_kap20_w6.html):

Noch flexibler wird die Makrodefinition durch die Verwendung des stringbildenden Operator (#), der manchmal auch String-Literal-Operator genannt wird. Wenn einem Makroparameter im Substitutionsstring ein # vorangestellt ist, wird das Argument beim Expandieren des Makros in einen String in Anführungszeichen umgewandelt. Wenn Sie also ein Makro wie folgt definieren:

#define AUSGEBEN(x) printf(#x)

und es mit folgender Anweisung aufrufen:

AUSGEBEN(Hallo Mama);

wird nach der Expansion daraus die Anweisung:

printf("Hallo Mama");

Die vom stringbildenden Operator durchgeführte Umwandlung berücksichtigt auch Sonderzeichen. Wenn es im Argument ein Zeichen gibt, das normalerweise ein Escape-Zeichen benötigt, fügt der #-Operator vor diesem Zeichen einen Backslash ein. Greifen wir dazu noch einmal auf unser obiges Beispiel zurück. Der Aufruf:

AUSGEBEN("Hallo Mama");

expandiert demnach zu

printf("\"Hallo Mama\"");

Ein Beispiel für den #-Operator finden Sie in Listing 20.1. Doch zuvor möchte ich Ihnen noch einen anderen Operator vorstellen, der in Makros verwendet wird, der Verkettungsoperator (##). Dieser Operator verkettet oder besser verbindet im Zuge der Makro-Expansion zwei Strings. Er verwendet keine Anführungszeichen und sieht auch keine Sonderbehandlung für Escape-Zeichen vor. Er dient hauptsächlich dazu, C-Quelltext-Sequenzen zu erzeugen. Wenn Sie zum Beispiel folgendes Makro definieren und aufrufen:

#define HACKEN(x) funk ## x
salat = HACKEN(3)(q, w);

wird das Makro, das in der zweiten Zeile aufgerufen wurde, folgendermaßen expandiert:

salat = funk3 (q, w);

Wie Sie sehen, ist es mit Hilfe des ##-Operators möglich, zwischen dem Aufruf verschiedener Funktionen auszuwählen. Sie programmieren praktisch die Erzeugung des C-Quellcodes.

Listing 20.1 zeigt eine Möglichkeit, den #-Operator zu verwenden.

Listing 20.1: Der #-Operator in der Makro-Expansion.

/* Einsatz des #-Operators in einer Makro-Expansion. */
 
#include <stdio.h>
 
#define AUSGABE(x) printf(#x " gleich %d.\n", x)
 
int main(void)
{
	int wert = 123;
	AUSGABE(wert);
	return 0;
}

Ausgabe:

wert gleich 123.

Durch die Verwendung des #-Operators in Zeile 5 wird der Variablenname wert bei der Expansion des Makros als String in Anführungszeichen an die Funktion printf() übergeben. Nach der Expansion sieht das Makro AUSGABE wie folgt aus:

printf("wert" " gleich %d.",  wert );

:!: Kompletes eBook habe ich unter http://wwwuser.gwdg.de/~kboehm/ebook/ entdeckt.


EOF