Reputation: 753
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
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
Reputation: 6526
When you launch the compiler, the following phases are (sequentially) performed:
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
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