David
David

Reputation: 753

C MACRO evaluation

I wish to declare a statically allocated array. Let's take a look at the following code:

#define MAX(a,b) ((a)>(b)?(a):(b))
#define FAST            16
#define SLOW            6
#define MAX_NUM         MAX(FAST,SLOW)
U8*   pBuffers[MAX_NUM];

When MAX_NUM being evaluated by the GCC compiler(FAST and SLOW are constants)? I would like to make sure that MAX_NUM is constant and being evaluated as part of a compilation or by the pre-processor.

Upvotes: 1

Views: 5138

Answers (3)

zwol
zwol

Reputation: 140806

The C standard requires the sizes of most arrays to be declared using an integer constant expression, which can be, and in this case is required to be, fully evaluated at compile time. (The only exception is "variable length arrays", and those have to be function-local variables with "automatic storage duration" — not statically allocated.)

Therefore, one answer to your question is you don't have to worry about it. If you write

WHATEVER_TYPE variable[SOME EXPRESSION];

at file scope, either SOME EXPRESSION will be evaluated to a constant at compile time, or compilation will fail and you will get an error.

But a more useful answer is to explain how to see for yourself whether SOME EXPRESSION is an integer constant expression, when you are reading code. First, you have to mentally expand all of the macros. Then, you will presumably have an arithmetic expression of some sort (if not, it's a syntax error).

This arithmetic expression is a constant expression if it has no side-effects, doesn't make any function calls, and doesn't refer to the value of any variable (not even if it is const) (enum constants are fine, though, as are string literals, and sizeof variable as long as variable is completely declared and isn't a variable-length array). It is an integer constant expression if, in addition, it doesn't try to do any floating-point or pointer arithmetic (you are allowed to write a floating-point literal as the immediate operand of a cast, though; for instance ((int)3.1415926) is an integer constant expression).

So, to take your example,

#define MAX(a,b) ((a)>(b)?(a):(b))
#define FAST            16
#define SLOW            6
#define MAX_NUM         MAX(FAST,SLOW)
U8*   pBuffers[MAX_NUM];

after macro expansion we have

U8* pBuffers[((16)>(6)?(16):(6))];

The expression inside the square brackets has no side effects, doesn't make any function calls, doesn't refer to the value of any variable, and doesn't do any floating-point or pointer arithmetic, so it's an integer constant expression, and the compiler is required to evaluate it at compile time.

By contrast, if you were using this definition of MAX instead:

static inline size_t MAX(size_t a, size_t b)
{ return a > b ? a : b; }

then macro expansion would produce

U8* pBuffers[MAX(16, 8)];

and the expression inside the square brackets would be making a function call, so it wouldn't be an integer constant expression, or even a constant expression, and you would get a compile-time error.

(FYI, the rules in C++ are much more complicated; if you need to know about that, ask a new question.)

Upvotes: 2

Laurent H.
Laurent H.

Reputation: 6526

When you launch the compiler, the following phases are (sequentially) performed:

  • preprocessing: it manages #define, #ifdef / #endif...
  • code generation: it produces the machine code runnable on target CPU
  • optimization: it optimizes depending on user options

During the preprocessing phase, the preprocessor will for example "replace" your line with:

U8*   pBuffers[MAX(FAST,SLOW)]

then:

U8*   pBuffers[((FAST)>(SLOW)?(FAST):(SLOW))]

then finally:

U8*   pBuffers[((16)>(6)?(16):(6))]

Indeed, the preprocessor is not very clever and does not go further.

During the code generation phase, your line will be interpreted as:

U8*   pBuffers[16]

Because the code generator is very clever.

Upvotes: 3

Umair
Umair

Reputation: 344

MACROS are always evaluated before the compilation process begins. So this code has nothing to worry about and it should work fine.

At the same time, this whole thing is the compiler dependent, I believe with gcc it will work fine. Maybe, for some bare-metal application, it may give a warning.

Upvotes: 0

Related Questions