2014-01-23 5 views
5

Für maximal zwei Zahlen bekommen, ich habe folgende MakrosWelches der folgenden Makros ist sicher und warum?

#define max(a,b) ((a) > (b) ? (a) : (b)) 

#define maxint(a,b) ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) 

Was ist der Unterschied zwischen oben beides. welches ist besser zu benutzen und warum. Ich fand diese Informationen der Makros here. Aber unfähig es zu verstehen.

+5

Denken Sie darüber nach, was vom Präprozessor mit 'max (++ i, --j)' erzeugt wird. Und * weder * ist "sicher", aber Letzteres ist nicht so anfällig für Nebenwirkungen im Zusammenhang mit Makro-Parameter-Erweiterung. – WhozCraig

+0

Zweitens ist sicherer. z.B. rufe 'max (i ++, j ++) 'oder' max '(fn1(), fn2())' – anishsane

+2

auf. Beachten Sie, dass das zweite dieser Makros eine 'gcc' Erweiterung verwendet (eine Block-Anweisung als Ausdruck behandelt) und ist daher nicht tragbar. – templatetypedef

Antwort

7

Das zweite Makro ist sicherer, sondern verwendet eine Nicht-Standard-C-Erweiterung von GCC zur Verfügung gestellt passieren würde: statement expressions. Aber der erste Ausdruck ist "generisch". Mit der typeof Erweiterung des GCC helfen würde:

#define mymax(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); \ 
         _a > _b ? _a : _b; }) 

zu kämpfen gegen die Ausgabe in JaredPar's answer angehoben Sie Präprozessor concatenation und GCC spezifischen __COUNTER__ (oder nur der mehr Standard __LINE__) verwenden könnten, z.B.

#define mymax_counted(a,b,c) ({typeof(a) _a##c = (a); \ 
           typeof(b) _b##c = (b); \ 
           _a##c > _b##c ? _a##c : _b##c; }) 
#define mymax(a,b) mymax_counted(a,b,__COUNTER__) 

aber auch das nicht ganz ausfallsicher ist (mit Pech, in einem gewissen Aufruf wie mymax(_a123,_b123) kollidieren könnte, wenn die einzigartigen __COUNTER__ zu diesem Zeitpunkt seine 123 passiert).

Eigentlich mit einem inline function besser ist (denn wenn man mymaxfun(i++,t[i]) das Verhalten nennen ist gut definiert und gibt das gleiche Ergebnis wie mymaxfun(t[i],i++), und weil optimierende Compiler würde Code so effizient wie erzeugen, wenn sie mit einem Makro):

static inline int mymaxfun(int a, int b) { return (a>b)?a:b; } 

Leider, C haben keine generischen Funktionen (in Betracht ziehen, für die Umstellung auf C++ mit seiner templates und std::max verwenden); jedoch C11 hat typgenerische Ausdrücke mit dem _Generic Stichwort

Makros nützlich sind (wenn gemeistert), aber Sie sollten sehr vorsichtig sein, über Nebenwirkungen in Argumente, wenn Makros Aufruf (zB mymax(i++,t[--i]++)), so sollten Sie immer Pflege und Dokument wenn ein Name ein Makro oder etwas anderes ist (wie eine Funktion). Als Faustregel gilt es, Nebeneffekte zu vermeiden - namentlich ++ oder --, aber auch viele andere - in funktionsaufrufenden Ausdrücken (sowohl Funktionsaufrufe als auch Makroaufrufe).

Schauen Sie sich die Präprozessor-erweiterte Form Ihres Quellcodes an; Führen Sie also für einen foo.c-Quellcode gcc -C -E foo.c > foo.i aus (fügen Sie alle Präprozessoroptionen hinzu, wie -I, -D usw.) und schauen Sie in das Verzeichnis foo.i, z. mit less foo.i; es ist immer lehrreich.

+0

Das einzige Problem ist, wenn jemand versucht, es mit Schwimmern zu verwenden. – woolstar

6

Das zweite Makro ist sicherer, weil es die Eingaben genau einmal auswertet. Dies ist wichtig, wenn die Eingänge Ausdrücke sind, die Nebenwirkungen haben zum Beispiel

max(i++, --j); 

Beachten Sie, dass ich sagte, sicherer und nicht sicher. Es ist immer noch möglich, dass dieses Makro inkorrekt ist, da sich im Bereich bereits Locals befinden können, die bereits _a und _ b heißen. Stellen Sie sich vor, was, wenn folgende ausgeführt wurde

int _a = 42; 
int _b = 13; 
maxint(_a, _b); 

Es wäre zu erweitern, um

int _a = 42; 
int _b = 13; 
{int _a = (_a), _b = (_b); _a > _b ? _a : _b; }) 
+1

Guter Punkt, obwohl ich bemerke, dass es eine * extrem * schlechte Programmierpraxis ist, aus diesem Grund eine lokale Variable mit einem Unterstrich zu beginnen. –

2

Keine sind sicher. Bitte vermeiden Sie auffällige max Makroimplementierungen, die auf nicht-standardmäßige C-Erweiterungen wie Ausdruckanweisungen angewiesen sind, die Ihnen alle möglichen Kopfschmerzen bereiten, wenn Sie Ihren Code jemals auf eine andere Plattform portieren müssen. Ich gebe mein Bestes, um diese Antwort zu präsentieren, da ich davon ausgehe, dass das Makro max meiner Meinung nach aufgrund seiner möglichen Nebenwirkungen eines der schlimmsten Dinge in C-Standardbibliotheken ist. In früheren Zeiten habe ich # undef ed max nur um auf der sicheren Seite zu sein.

Sie sind besser dran Codierung der zweite Makro maxint als eine Funktion, da es alle Nachteile von Makros hat (z. B. kann nicht leicht debuggen, instanziierte Variablen mit Einheimischen kollidieren), aber keiner der Vorteile (z. B. Generika).

Alternativen:

Mein Favorit: Verwenden Sie den ternären inline: a > b ? a : b wo Sie die Klammern fallen kann zu schmecken, da Sie genau wissen, was los ist. Es ist auch schneller als Makros, die versuchen, sicher zu sein, indem sie Wertkopien nehmen.

Erstellen Sie Ihre eigenen Funktionen. Erwägen Sie, max für integrierte Typen und fmax für Fließkommazahl zu definieren. Hier gibt es bereits einen Präzedenzfall mit abs und fabs.

+0

AFAIK, 'max' ist nicht in der C-Standardbibliothek ... –