Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C fixed size array treated as variable size

Tags:

c

c99

I have been trying to define a static array with size that should be known at compile time (it's a constant expression). It appears that gcc cannot determine the size of the array when it contains a floating point constant (and I get "storage size of ... isn’t constant").

Here is a minimal example:

int main(void)
{
    static int foo[(unsigned)(2 / 0.5)];
    return 0;
}

What is the reason for this behavior?

EDIT

I already have the answer I needed. I still don't understand the rationale behind not allowing that kind of expressions, but this is a separate question. I'll explain for the curious how I arrived at the problem.

It's about a game I'm writing as an excercise. Units move on a battlefield and I have divided the movement in steps. I have to remember the position of each unit on each step so that I can display animation later. The number of steps is chosen so that it ensures there will be a step on which units are close enough to fight each other but not so close as to collide. Here are the relevant pieces of code:

#define UNIT_SPEED_LIMIT 12
#define DISTANCE_MELEE 0.25
#define MOVEMENT_STEPS (unsigned)(2 * UNIT_SPEED_LIMIT / DISTANCE_MELEE)

struct position (*movements)[MOVEMENT_STEPS + 1];

Defining DISTANCE_MELEE (maximum distance at which close combat is possible) and using it to calculate the number of steps seems to be the natural way to proceed (more so because I use this constant in multiple contexts). Since I cannot define movements this way, I have to invent a concept like "number of steps for a single unit of distance" and use multiplication by int instead of division by double. I want to avoid dynamic memory allocation in order to keep the code simple.

like image 761
martinkunev Avatar asked Jun 27 '16 12:06

martinkunev


2 Answers

According to the publicly available C99 draft standard n1256, the syntax for array declaration is described by

6.7.5.2 Array declarators

2

An ordinary identifier (as defined in 6.2.3) that has a variably modified type shall have either block scope and no linkage or function prototype scope. If an identifier is declared to be an object with static storage duration, it shall not have a variable length array type.

4

If the size is not present, the array type is an incomplete type. If the size is * instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations with function prototype scope; 124) such arrays are nonetheless complete types. If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.

So the expression in the [] must be an integer constant expression for the array to be declarable with static storage duration. The standard has this to say about integer constant expressions:

6.6 Constant expressions

6

An integer constant expression 99) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

Unfortunately, (unsigned)(2 / 0.5) does not apply the cast immediately to a floating-point constant, but rather to an arithmetic constant expression. This does not constitute an integer constant expression, and is thus not permissible as the size of an array with static storage duration.

like image 164
EOF Avatar answered Nov 12 '22 14:11

EOF


OP's primary question is well answer here.

To address OP's higher level problem of how to use values like 0.5 or 0.25 in pre-processing, use fractional arithmetic:

#define UNIT_SPEED_LIMIT 12
// #define DISTANCE_MELEE 0.25
// use 25/100 or 1/4 or ...
#define DISTANCE_MELEE_N 1
#define DISTANCE_MELEE_D 4
// #define MOVEMENT_STEPS (unsigned)(2 * UNIT_SPEED_LIMIT / DISTANCE_MELEE)
#define MOVEMENT_STEPS (2u * UNIT_SPEED_LIMIT * DISTANCE_MELEE_D /  DISTANCE_MELEE_N)

struct position (*movements)[MOVEMENT_STEPS + 1];
like image 32
chux - Reinstate Monica Avatar answered Nov 12 '22 14:11

chux - Reinstate Monica