2010-12-15 9 views
0

Ich möchte ein Makro zur Kompilierzeit basierend auf dem Wert eines anderen Makros definieren. Allerdings wird dieser Code nicht wie erwartet ausgeführt wird:Weird Makrodefinition Problem

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIXTEEN 16 
#define TWO (SIXTEEN % 8 == 0)? (SIXTEEN/8) : ((SIXTEEN/8) + 1) 

int main(); 

int main() { 
    printf("max = %d\n", TWO); 
    int i; 
    for (i = 0; i < TWO; i++) { 
     printf("%d\n", i); 
    } 
    return 0; 
} 

Diese Drucke:

max = 2 
0 
1 
2 
... 

und fährt fort, bis sie beendet, wenn es einfach Drucken werden sollten:

max = 2 
0 
1 

und Aussteigen.

Wenn ich diese stattdessen tun, es funktioniert:

#define TWO 2 

Ich dachte, dass dies ein Problem mit der Definition der Makro war ... aber wenn ich folgendes mit dem ursprünglichen #define tun, es scheint Arbeit:

... 
int count = TWO; 
for (i = 0; i < count; i++) { 
... 

Kann mir jemand erklären, was hier vor sich geht?

Antwort

11

Das Problem ist, dass das Token TWO durch die Token, mit dem Sie das Makro definiert ersetzt wird, so folgt aus:

i < TWO 

wird dies:

i < (SIXTEEN % 8 == 0)? (SIXTEEN/8) : ((SIXTEEN/8) + 1) 

Wegen Operator Vorrang, das ist lesen Sie als:

(i < (SIXTEEN % 8 == 0)) 
    ? (SIXTEEN/8) 
    : ((SIXTEEN/8) + 1) 

Sie benötigen zusätzliche Klammern, so dass, wenn TWO durch seine Ersatzliste ersetzt wird, erhalten Sie das gewünschte Ergebnis:

#define TWO ((SIXTEEN % 8 == 0)? (SIXTEEN/8) : ((SIXTEEN/8) + 1)) 
      ^             ^

Wenn Makros verwenden, ist es am besten Klammern zu verwenden, wo immer Sie können das Ergebnis, um sicherzustellen, ist das, was Sie erwarten.

+0

+1 für mich um 37 Sekunden zu schlagen. = ( –

+2

+1) Die allgemeine Faustregel lautet: Wenn Ihr Makro in einen Ausdruck expandieren soll, stellen Sie sicher, dass es in einem (übereinstimmenden) Paar von Parens eingeschlossen ist. Die einzige Ausnahme ist, wenn es garantiert zu einem einzigen Token erweitert wird (wie das SECHZEHN Makro des OP). –

1

das Makro erweitern und nach der Makroerweiterung zu Ihrer for Schleife aussehen:

for (i = 0; i < (16 % 8 == 0)? (16/8) : ((16/8) + 1); i++) 

sehen? i < (16 % 8 == 0) ist der Zustand des ?: Operators. Sie müssen ein Klammerpaar um die Definition TWO setzen.

2

Schließen Sie immer Makros in Klammern ein, da Sie nicht immer den Kontext kennen, in dem es verwendet wird - es kann vorkommen, dass ein benachbarter Operator eine höhere Priorität hat und das Makro nicht korrekt ausgewertet wird.

Nicht Klammern verwendet, ist nur gerechtfertigt, wenn die #define ‚d Symbol ein einzelnes Token ist, wie 5 oder "hello world".

Wenn Sie in Erwägung ziehen, Ihr Makro mit Argumenten aufzurufen, bei denen es sich um Ausdrücke und nicht nur um einzelne Token handelt, schließen Sie jedes Argument dieses Arguments aus demselben Grund wie oben in Klammern ein.

Eine andere Sache zu vermeiden ist die Weitergabe von Ausdrücken, die Nebenwirkungen als Makroargumente haben.Wenn das entsprechende Makroargument in seiner Definition mehr als einmal referenziert wird, wird die Auswertung mehr als einmal durchgeführt, was normalerweise nicht gewünscht ist.