Ich bin ein erstes Jahr Informatik - Student und mein Professor sagte , #define
ist in den Industriestandards verboten zusammen mit #if
, #ifdef
, #else
, und ein paar andere Präprozessordirektiven. Er benutzte das Wort "verboten" wegen unerwarteten Verhaltens.
Ist das richtig? Wenn ja warum?
Gibt es tatsächlich Standards, die die Verwendung dieser Richtlinien verbieten?
c
c-preprocessor
preprocessor-directive
psrag anvesh
quelle
quelle
#define
und so weiter sind weit verbreitet. Manchmal zu weit verbreitet, aber definitiv verwendet. Es gibt Stellen, an denen der C-Standard die Verwendung von Makros vorschreibt - diese können Sie nicht einfach vermeiden. Sie können die MISRA C-Standards überprüfen. Sie neigen dazu, Dinge zu verbieten, aber ich bin mir ziemlich sicher, dass selbst sie erkennen, dass#define
et al manchmal benötigt werden.Antworten:
Zuerst habe ich davon gehört.
Nein;
#define
und so weiter sind weit verbreitet. Manchmal zu weit verbreitet, aber definitiv verwendet. Es gibt Stellen, an denen der C-Standard die Verwendung von Makros vorschreibt - diese können Sie nicht einfach vermeiden. Zum Beispiel sagt §7.5 Fehler<errno.h>
:Angesichts dessen ist klar, dass nicht alle Industriestandards die Verwendung der Makroanweisungen für C-Präprozessoren verbieten. Es gibt jedoch Standards für bewährte Verfahren oder Codierungsrichtlinien von verschiedenen Organisationen, die Beschränkungen für die Verwendung des C-Präprozessors vorschreiben, obwohl keiner seine Verwendung vollständig verbietet - er ist ein angeborener Teil von C und kann nicht vollständig vermieden werden. Diese Standards gelten häufig für Personen, die in sicherheitskritischen Bereichen arbeiten.
Ein Standard, den Sie überprüfen können, ist der MISRA C (2012) -Standard. das neigt dazu, Dinge zu verbieten, aber selbst das erkennt, dass
#define
manchmal et al. benötigt werden (Abschnitt 8.20, Regeln 20.1 bis 20.14 behandeln den C-Präprozessor).Die C-Codierungsstandards der NASA GSFC (Goddard Space Flight Center) C sagen einfach:
Die Diskussion nach dieser einleitenden Erklärung veranschaulicht die akzeptable Verwendung von Funktionsmakros.
Der CERT C-Codierungsstandard enthält eine Reihe von Richtlinien zur Verwendung des Präprozessors und impliziert, dass Sie die Verwendung des Präprozessors minimieren sollten, seine Verwendung jedoch nicht verbieten.
Stroustrup möchte den Präprozessor in C ++ irrelevant machen, aber das ist noch nicht geschehen. Wie Peter bemerkt , schreiben einige C ++ - Standards, wie die JSF AV C ++ - Codierungsstandards ( Joint Strike Fighter, Luftfahrzeug ) von ca. 2005, eine minimale Verwendung des C-Präprozessors vor. Im Wesentlichen beschränken sich die JSF AV C ++ - Regeln auf
#include
und#ifndef XYZ_H
/#define XYZ_H
/… /#endif
dance, wodurch mehrere Einschlüsse eines einzelnen Headers verhindert werden. C ++ verfügt über einige Optionen, die in C nicht verfügbar sind - insbesondere eine bessere Unterstützung für typisierte Konstanten, die dann an Stellen verwendet werden können, an denen C die Verwendung nicht zulässt. Siehe auchstatic const
vs#define
vsenum
für eine Diskussion der dortigen Probleme.Es ist eine gute Idee, die Verwendung des Präprozessors zu minimieren - er wird häufig mindestens so oft missbraucht wie er verwendet wird (siehe die 'Bibliothek' des Boost- Präprozessors für Abbildungen, wie weit Sie mit dem C-Präprozessor gehen können).
Zusammenfassung
Der Präprozessor ist ein integraler Bestandteil von C
#define
und#if
usw. kann nicht vollständig vermieden werden. Die Erklärung des Professor in der Frage ist nicht allgemein gültig:#define
wird zusammen mit den Industrie - Standards verboten#if
,#ifdef
,#else
, und ein paar andere Makros ist eine Over-Anweisung am besten, aber vielleicht mit ausdrücklicher Bezugnahme auf bestimmte Industriestandards erträglich sein (aber Die fraglichen Normen enthalten nicht ISO / IEC 9899: 2011 (die C-Norm).Beachten Sie, dass David Hammen Informationen zu einem bestimmten C-Codierungsstandard bereitgestellt hat - dem JPL C-Codierungsstandard -, der viele Dinge verbietet, die viele Menschen in C verwenden, einschließlich der Einschränkung der Verwendung des C-Präprozessors (und der Einschränkung der Verwendung des dynamischen Speichers) Zuordnung und Verbot der Rekursion - lesen Sie es, um herauszufinden, warum und entscheiden Sie, ob diese Gründe für Sie relevant sind.
quelle
#define MAX(a,b) ((a>b)?(a):(b))
ist in Ordnung, trotz seiner möglichen Gefahren.Nein, die Verwendung von Makros ist nicht verboten.
Tatsächlich ist die Verwendung von
#include
Schutzvorrichtungen in Header-Dateien eine gängige Technik, die häufig obligatorisch ist und durch anerkannte Codierungsrichtlinien gefördert wird. Einige Leute behaupten, dass dies#pragma once
eine Alternative dazu ist, aber das Problem ist, dass#pragma once
- per Definition, da Pragmas ein Haken sind, der vom Standard für compilerspezifische Erweiterungen bereitgestellt wird - nicht Standard ist, selbst wenn er von einer Reihe von Compilern unterstützt wird.Es gibt jedoch eine Reihe von Branchenrichtlinien und empfohlenen Praktiken, die die Verwendung von Makros außer
#include
Wachen aufgrund der Probleme, die Makros mit sich bringen (ohne Berücksichtigung des Umfangs usw.), aktiv unterbinden. In der C ++ - Entwicklung wird die Verwendung von Makros noch stärker verpönt als in der C-Entwicklung.Von der Verwendung von etwas abzuraten ist nicht dasselbe wie es zu verbieten, da es immer noch möglich ist, es rechtmäßig zu verwenden - zum Beispiel durch Dokumentation einer Begründung.
quelle
Einige Codierungsstandards entmutigen oder sogar verbieten die Verwendung von
#define
erstellen Funktionsmakros , die Argumente, wie#define SQR(x) ((x)*(x))
weil a) solche Makros nicht typsicher sind und b) unweigerlich jemand schreibt
SQR(x++)
, was schlecht ist, Juju.Einige Standards können die Verwendung von
#ifdef
s für die bedingte Kompilierung verhindern oder verbieten . Der folgende Code verwendet beispielsweise die bedingte Kompilierung, um einensize_t
Wert ordnungsgemäß auszudrucken . Für C99 und höher verwenden Sie den%zu
Konvertierungsspezifizierer. Für C89 und früher verwenden Sie%lu
den Wert und wandeln ihn in Folgendes umunsigned long
:#if __STDC_VERSION__ >= 199901L # define SIZE_T_CAST # define SIZE_T_FMT "%zu" #else # define SIZE_T_CAST (unsigned long) # define SIZE_T_FMT "%lu" #endif ... printf( "sizeof foo = " SIZE_T_FMT "\n", SIZE_T_CAST sizeof foo );
Einige Standards schreiben möglicherweise vor , dass Sie das Modul stattdessen zweimal implementieren, einmal für C89 und früher, einmal für C99 und höher:
/* C89 version */ printf( "sizeof foo = %lu\n", (unsigned long) sizeof foo ); /* C99 version */ printf( "sizeof foo = %zu\n", sizeof foo );
Lassen Sie dann Make (oder Ant oder das von Ihnen verwendete Build-Tool) das Kompilieren und Verknüpfen der richtigen Version durchführen. Für dieses Beispiel , die lächerlich übertrieben wäre, aber ich habe Code gesehen , dass ein unauffindbar Rattennest von war
#ifdef
s, der sollte die bedingte Code in separate Dateien ausgeklammert hatten.Mir sind jedoch keine Unternehmen oder Branchengruppen bekannt, die die Verwendung von Präprozessoranweisungen vollständig verboten haben .
quelle
Makros können nicht "gebannt" werden. Die Aussage ist Unsinn. Buchstäblich.
Zum Beispiel erfordert Abschnitt 7.5 Fehler
<errno.h>
des C-Standards die Verwendung von Makros:Makros sind also nicht nur ein erforderlicher Bestandteil von C, in einigen Fällen führt ihre Nichtverwendung zu undefiniertem Verhalten.
quelle
errno
ohne das Makro zu verwenden, ohne UB aufzurufen? Der Standard sagt eindeutig, dasserrno
es sich um ein Makro handelt, und es besagt eindeutig, dass daserrno
Makro unterdrückt oder auf andere Weise Ihre eigenenerrno
Ergebnisse in UB definiert werden. Und wie kann man im Allgemeinen robusten Code schreiben, ohne ihn zu verwendenerrno
? Einige Systemaufrufe können weiterhin unterbrochen werden und einen Fehler miterrno
set to zurückgeben,EINTR
selbst wenn alle Ihre SignalflagsSA_RESTART
gesetzt sind. Ergo, wenn Sie verwendenerrno
, können Sie nicht ohne Makros codieren, es sei denn, Sie rufen UB auf.errno
ohne das erforderliche Makro zu verwenden, rufen Sie UB auf. Klar?Nein,
#define
ist nicht verboten. Missbrauch von#define
kann jedoch verpönt sein.Zum Beispiel können Sie verwenden
#define DEBUG
in Ihrem Code, damit Sie später Teile Ihres Codes für die bedingte Kompilierung
#ifdef DEBUG
nur für Debug-Zwecke festlegen können. Ich glaube nicht, dass jemand, der bei klarem Verstand ist, so etwas verbieten möchte. Mit definierte Makros#define
werden auch häufig in tragbaren Programmen verwendet, um die Kompilierung von plattformspezifischem Code zu aktivieren / deaktivieren.Wenn Sie jedoch so etwas verwenden
#define PI 3.141592653589793
Ihr Lehrer kann zu Recht darauf hinweisen, dass es viel besser ist,
PI
eine Konstante mit dem entsprechenden Typ zu deklarieren , z.const double PI = 3.141592653589793;
da es dem Compiler ermöglicht, die Typprüfung durchzuführen, wenn sie
PI
verwendet wird.In ähnlicher Weise (wie oben von John Bode erwähnt) kann die Verwendung von funktionsähnlichen Makros abgelehnt werden, insbesondere in C ++, wo Vorlagen verwendet werden können. Also statt
#define SQ(X) ((X)*(X))
erwägen, zu verwenden
double SQ(double X) { return X * X; }
oder, in C ++, noch besser,
template <typename T>T SQ(T X) { return X * X; }
Wieder einmal besteht die Idee darin, dass Sie dem Compiler durch die Verwendung der Funktionen der Sprache anstelle des Präprozessors erlauben, die Prüfung zu tippen und (möglicherweise) besseren Code zu generieren.
Sobald Sie über ausreichende Programmiererfahrung verfügen, wissen Sie genau, wann die Verwendung angemessen ist
#define
. Bis dahin halte ich es für eine gute Idee, wenn Ihr Lehrer bestimmte Regeln und Kodierungsstandards auferlegt, aber vorzugsweise sollten sie selbst die Gründe kennen und erklären können. Ein generelles Verbot#define
ist unsinnig.quelle
Das ist völlig falsch, Makros werden in C häufig verwendet. Anfänger verwenden sie oft schlecht, aber das ist kein Grund, sie aus der Industrie zu verbannen. Eine klassische schlechte Verwendung ist
#define succesor(n) n + 1
. Wenn Sie erwarten2 * successor(9)
, 20 zu geben, liegen Sie falsch, da dieser Ausdruck als2 * 9 + 1
19 und nicht als 20 übersetzt wird. Verwenden Sie Klammern, um das erwartete Ergebnis zu erhalten.quelle
#define successor(n) ((n) + 1)
Nein, es ist nicht verboten. Und um ehrlich zu sein, es ist unmöglich, nicht trivialen Multi-Plattform-Code ohne ihn zu erstellen.
quelle
Nein, Ihr Professor liegt falsch oder Sie haben etwas falsch gehört.
#define
ist ein Präprozessor-Makro, und Präprozessor-Makros werden für die bedingte Kompilierung und einige Konventionen benötigt, die nicht einfach in der C-Sprache erstellt wurden. Beispielsweise wurde in einem neueren C-Standard, nämlich C99, die Unterstützung für Boolesche Werte hinzugefügt. Aber es wird nicht "native" von der Sprache unterstützt, sondern von Präprozessoren#define
. Siehe diesen Verweis auf stdbool.hquelle
*NULL
oderchar foo[2]; foo[2];
alle Programmierfehler verursacht. Um besser zu wissen, was undefiniertes Verhalten ist, empfehle ich Folgendeserrno
- der Standard besagt ausdrücklich, dass dieerrno
Nichtverwendung des Makros zu undefiniertem Verhalten führt.Makros werden in GNU Land C ziemlich häufig verwendet, und ohne bedingte Präprozessorbefehle gibt es keine Möglichkeit, mehrere Einschlüsse derselben Quelldateien ordnungsgemäß zu verarbeiten, sodass sie mir als wesentliche Sprachfunktionen erscheinen.
Vielleicht ist Ihre Klasse tatsächlich in C ++, was trotz des Versäumnisses vieler Leute von C unterschieden werden sollte, da es eine andere Sprache ist und ich dort nicht für Makros sprechen kann. Oder vielleicht meinte der Professor, er verbiete sie in seiner Klasse. Ich bin mir jedenfalls sicher, dass die SO-Community daran interessiert sein würde, über welchen Standard er spricht, da ich mir ziemlich sicher bin, dass alle C-Standards die Verwendung von Makros unterstützen.
quelle
#ifndef HEADER_H #define HEADER_H ... #endif
)#pragma once
? Schauen Sie auf jeden Fall nach - es hat es noch nicht in den Standard geschafft, aber es wird von jedem Anbieter (GCC, Clang, EDG, MSVC, Intel, Green Hills, ARM, ...) unterstützt und verringert die Wahrscheinlichkeit, dumme Tippfehler zu machen, erheblich#ifndef FOO_H #define FOOH
oder#ifdef FOO_H #define FOO_H
oder dasselbe Makro in zwei verschiedenen .h-Dateien verwenden. (Es verbessert auch die Kompilierungszeiten und beseitigt heilige Kriege um den Abstand und das Einrücken von#ifndef
Wachen im Stil.) Die bedingte Kompilierung hat zwar ihre Verwendung, aber "arme Männer#pragma once
" gehören nicht dazu.#pragma
. Es ist daher einfach eine Fantasie zu behaupten, dass jedes Pragma jemals standardisiert wird - der Begriff steht völlig im Widerspruch zu dem, was Pragmas sind. Die Tatsache, dass mehrere Anbieter einige ihrer herstellerspezifischen Funktionen standardmäßig falsch darstellen, macht dies nicht möglich.Im Gegensatz zu allen bisherigen Antworten ist die Verwendung von Präprozessor-Direktiven im hochzuverlässigen Computing häufig verboten. Hiervon gibt es zwei Ausnahmen, deren Verwendung in solchen Organisationen vorgeschrieben ist. Dies sind die
#include
Direktive und die Verwendung eines Include-Schutzes in einer Header-Datei. Diese Art von Verboten ist in C ++ wahrscheinlicher als in C.Hier nur ein Beispiel: 16.1.1 Verwenden Sie den Präprozessor nur zum Implementieren von Include-Guards und zum Einschließen von Header-Dateien mit Include-Guards .
Ein weiteres Beispiel, diesmal für C statt C ++: JPL Institutional Coding Standard für die Programmiersprache C . Dieser C-Codierungsstandard geht nicht ganz so weit, die Verwendung des Präprozessors vollständig zu verbieten, kommt aber nahe. Insbesondere heißt es
Ich kann diese Standards weder dulden noch entschlüsseln. Aber zu sagen, dass sie nicht existieren, ist lächerlich.
quelle
Wenn Ihr C-Code mit C ++ - Code zusammenarbeiten soll, müssen Sie Ihre extern sichtbaren Symbole, z. B. Funktionsdeklarationen, im
extern "C"
Namespace deklarieren . Dies erfolgt häufig mithilfe der bedingten Kompilierung:#ifdef __cplusplus extern "C" { #endif /* C header file body */ #ifdef __cplusplus } #endif
quelle
#include "c++/header.h"
im C ++ - Code geschehen, in dem die Datei Folgendes"c++/header.h"
enthält:extern "C" { / #include "c/header.h" / }
(mit Solo-Schrägstrichen, die Zeilenumbrüche anzeigen - und die Auswahl der Unterverzeichnisnamen ist natürlich weitgehend willkürlich), wodurch eine bedingte Kompilierung auf Kosten von a vermieden würde zusätzliche Datei. Ich stimme zu, was Sie zeigen, ist das, was am häufigsten verwendet wird (ich benutze es selbst). Aber es ist eigentlich nicht notwendig, wenn Sie die Dinge richtig eingerichtet haben.#if
" -Anforderung. Zwei Header-Dateien, von denen eine nur die andere enthält, erzeugen nur Fragen.Schauen Sie sich eine Header-Datei an und Sie werden ungefähr Folgendes sehen:
#ifndef _FILE_NAME_H #define _FILE_NAME_H //Exported functions, strucs, define, ect. go here #endif /*_FILE_NAME_H */
Diese Definitionen sind nicht nur zulässig, sondern auch von entscheidender Bedeutung, da jedes Mal, wenn auf die Header-Datei in Dateien verwiesen wird, diese separat aufgenommen wird. Dies bedeutet, dass Sie ohne die Definition alles zwischen den Wachen mehrmals neu definieren. Der beste Fall lässt sich nicht kompilieren, und im schlimmsten Fall kratzen Sie sich später am Kopf, warum Ihr Code nicht so funktioniert, wie Sie es möchten.
Der Compiler verwendet auch define, wie hier gezeigt, mit gcc , mit dem Sie beispielsweise die Version des Compilers testen können, die sehr nützlich ist. Ich arbeite derzeit an einem Projekt, das mit avr-gcc kompiliert werden muss, aber wir haben eine Testumgebung, in der wir auch unseren Code ausführen. Um zu verhindern, dass die avr-spezifischen Dateien und Register unseren Testcode nicht ausführen, gehen wir wie folgt vor:
#ifdef __AVR__ //avr specific code here #endif
Wenn dies im Produktionscode verwendet wird, kann der ergänzende Testcode ohne Verwendung von avr-gcc kompiliert werden, und der obige Code wird nur mit avr-gcc kompiliert.
quelle
Wenn Sie gerade erwähnt hätten
#define
, hätte ich gedacht, dass er vielleicht auf die Verwendung für Aufzählungen anspielt, die besser geeignet sindenum
, um dumme Fehler wie das zweimalige Zuweisen desselben numerischen Werts zu vermeiden.Beachten Sie, dass es selbst in dieser Situation manchmal besser ist,
#define
s als Aufzählungen zu verwenden, beispielsweise wenn Sie sich auf numerische Werte verlassen, die mit anderen Systemen ausgetauscht werden, und die tatsächlichen Werte auch dann gleich bleiben müssen, wenn Sie Konstanten hinzufügen / löschen (aus Kompatibilitätsgründen).Jedoch , dass das Hinzufügen
#if
,#ifdef
usw. sollten ebenfalls nicht verwendet werden , ist einfach seltsam. Natürlich sollten sie wahrscheinlich nicht missbraucht werden, aber im wirklichen Leben gibt es Dutzende Gründe, sie zu benutzen.Was er möglicherweise gemeint hat, könnte sein, dass Sie (wo zutreffend) das Verhalten in der Quelle nicht fest codieren sollten (was eine Neukompilierung erfordern würde, um ein anderes Verhalten zu erhalten), sondern stattdessen eine Form der Laufzeitkonfiguration verwenden sollten.
Das ist die einzige Interpretation, die mir in den Sinn kommen würde.
quelle