20

I have a habit of using the following syntax in my compile-time flags:

#if (defined(A) & defined(B))

It's usually suggested that I do it with the && as follows:

#if (defined(A) && defined(B))

I know the difference between the two operators, and that in normal code && would short-circuit. However, the above is all handled by the compiler. Does it even matter what I use? Does it affect compile time by some infinitesimal amount because it doesn't evaluate the second define()?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Catsunami
  • 569
  • 6
  • 20

3 Answers3

33

Since defined(SOMETHING) yields 0 or 1, so that you're guaranteed 0 or 1 on both sides, it doesn't make a technical difference whether you use & or &&.

It's mostly about good habits (using & could carry over to some situation where it would be wrong) and about writing code that is easy to grasp by simple pattern matching. A & in there causes a millisecond pause while one considers whether it possibly could be a bit-level thing.

On the third hand, you can't use keyword and, which you ¹can use in ordinary C++ code.

Notes:
¹ With Visual C++ you can use and via a forced include of <iso646.h>.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I probably mistakenly thought the standard didn't support `and` in preprocessor conditionals, but it supports any integral constant expression (C++11 §16.1 "Conditional inclusion"). Fixed by overstrike. It's unclear because it says " except that identifiers (including those lexically identical to keywords) are interpreted as described below". – Cheers and hth. - Alf Sep 02 '16 at 05:25
  • 1
    Yeah, it is not stated clearly, but `and` is not an identifier so the normal rules for identifiers do not apply. –  Sep 02 '16 at 05:45
  • 2
    MSVC has unofficially deprecated the ISO646 alternative tokens. I'm not really sure why, but they cannot be used (*anywhere*, not just in preprocessor conditionals) unless you manually include the header. Alternatively, you can throw the `/Ze` switch (which disables Microsoft language extensions, enabled by default with the `/Za` switch), and then these alternative tokens will be available everywhere without the need to include the header. – Cody Gray - on strike Sep 02 '16 at 10:30
  • @CodyGray This sounds like you and/or MSVC have confused C with C++. The ISO646 alternative tokens have never been available in any context in C, unless you include ``, which defines them all as macros expanding to the traditional equivalent. Conversely, in C++, they are supposed to be usable everywhere without including any header. – zwol Sep 02 '16 at 16:11
17

According to the C99 standard, the expressions used in the preprocessor are constant expressions as defined by the C language itself, and are evaluated using the same engine. Therefore, && is a logical and operator that short circuits based on its LHS, and & is a bitwise operator with no predefined order of evaluation.

In practical terms, when used with defined() as you are, there is no difference between the two. However, the following would show a difference:

#define A 2
#define B 5
#if (A && B)
printf("A && B\n");
#endif
#if (A & B)
printf("A & B"\n);
#endif

In this case, A && B will be output, but not A & B (since the result of that bitwise-and is 0)

J Earls
  • 1,792
  • 8
  • 12
3

I would like to add to the previous answers that it can actually matter a lot in a situation like this:

#define A 0
#define B 21
#if (A != 0) && (42 / A == B)
/* ... */
#endif

Here, if A == 0, the compiler will not break. Writing (A != 0) & (42 / A == B) will make the compiler complain about a division by zero.

madmann91
  • 91
  • 4