dhDPfKCfJdU4JIW4Sc
dhDPfKCfJdU4JIW4Sc

Reputation: 68

C - Macro contradiction

I just started learning C and I am not sure about how everything works. Some of examples simply looks weird to me and I cannot predict what would it print. I am programming in GCC, I mean it is my compiler, so every code I test is in GCC.

I understood pointers etc how that all stuff work, but also as you know there are macros. Macros are very weird, and I discovered one example I have no explanation about. For example look at this code particular code:

#include <stdio.h>

int main(void){

  #define A 1

  if(A == 0){
    #define B 1
  }

  #ifdef B
    printf("B is defined, which means that A == 0.\n");
  #elif
    printf("B isn't defined, which means that A == 1.\n");
  #endif

  printf("Value of A is %d\n", A);

  return 0;
}

What should it print? Well, I though it is easy to guess, but now I see that it is not very easy. The result is not what i thought is.

This was my reasoning: we firstly define A to be 1 and then if A is 0 then define B to be 1. So, if B is defined it means that A == 0 and otherwise A == 1. But, surprisingly it prints:

B is defined, which means that A == 0.
Value of A is 1

What? It just printed a contradiction. I am not sure if this is some weird thing in GCC (because I have no visual studio or other compiler to test it). However, I am 99% sure I somehow misunderstood macros. I tried using variables instead of macros and it works properly.

Is it an issue, or did I simply misunderstand how macros work? Please keep in mind that I'm C beginner, so take it easy :).

Thank you in advance.

Upvotes: 2

Views: 127

Answers (3)

chux
chux

Reputation: 153447

The #define B 1 does not occur at run time. It happens at compile time. So #define B 1 is part of the compilation regardless of the result of A == 0, a run-time comparison

  if(A == 0){
    #define B 1
  }

The following code

  #ifdef B
    printf("B is defined, which means that A == 0.\n");
  #elif
    printf("B isn't defined, which means that A == 1.\n");
  #endif

is then the same as

  printf("B is defined, which means that A == 0.\n");

Upvotes: 1

Grifplex
Grifplex

Reputation: 302

Macros are part of the preprocessor before the code is actually compiled. The compiler will search the code for all preprocessor directives and modify the code accordingly. So when the compiler gets to if(A == 0) {#define B 1} it has already been expanded to if(0 == 1) { }.

Macros makes it easy to configure an aspect of your code or to replace magic numbers into meaningful defines. For example:

#define DO_HOUR_CALCULATION

#define NUMBER_OF_SECONDS_PER_MINUTE (60)
#define NUMBER_OF_SECONDS_PER_HOUR (NUMBER_OF_SECONDS_PER_MINUTE * 60)
...
#ifdef DO_HOUR_CALCULATION
int hours = time / NUMBER_OF_SECONDS_PER_HOUR;
int minutes = (time % NUMBER_OF_SECONDS_PER_HOUR) / NUMBER_OF_SECONDS_PER_MINUTE;
#endif // defined(DO_TIME_CALCULATION)

Another use is to simplify a repeated or configurable task:

#define DEBUG

#ifdef DEBUG
#define DPRINTF(fmt, args...) printf(fmt, ## args)
#else
#define DPRINTF(args...)   do {} while (0)
#endif
...
ret = read(fd, p_buffer, buf_size);
DPRINTF("read(fd) returned %d\n", ret);

Hope this helps you and happy coding!

Upvotes: 4

Stephan Lechner
Stephan Lechner

Reputation: 35154

Preprocessing directives are processed before non-preprocessing tokens are translated. And particularly, preprocessing directives are always evaluated at compile time, and never at runtime (cf. C standard, e.g. this draft). If a token is a preprocessing token is defined as follows (all others are non-preprocessing tokens):

6.10 Preprocessing directives

(2) A preprocessing directive consists of a sequence of preprocessing tokens that begins with a # preprocessing token that (at the start of translation phase 4) is either the first character in the source file (optionally after white space containing no new-line characters) or that follows white space containing at least one new-line character

So if(A == 0) are non-preprocessing tokens (as the respective line does not start with a #, optionally with some space characters before), while #define B 1 is a preprocessing token. According to the translation phases, preprocessing tokens are evaluated at translation time before the other tokens:

5.1.1.2 Translation phases

(4) Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed.

(7) ... The resulting tokens are syntactically and semantically analyzed and translated as a translation unit.

So B will be defined already before if(A==0) will be translated, and particularly before if(A==0) will be executed. Thus, B will always be defined in your program.

Upvotes: 2

Related Questions