10

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.

martinkunev
  • 1,364
  • 18
  • 39
  • Clean compilation with MSVC, giving `sizeof foo` as `16`. – Weather Vane Jun 27 '16 at 12:32
  • Why do you want to do it? – Sourav Ghosh Jun 27 '16 at 12:33
  • 13
    C99 draft standard n1256: *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.* – EOF Jun 27 '16 at 12:36
  • Possible duplicate of [How to create an array when the size is a variable not a constant?](http://stackoverflow.com/questions/14459248/how-to-create-an-array-when-the-size-is-a-variable-not-a-constant) – Tomasz Plaskota Jun 27 '16 at 12:41
  • Some alternatives: - You may use shifts with integer values to multiply or divide the value; - You can use `alloca()`; - Even `malloc()` may be used, as `gcc` will probably optimize it and convert it to `alloca()` or even "regular" allocation if it determines the buffer won't be used outside the function. – pah Jun 27 '16 at 12:42
  • 3
    @WeatherVane floating point expressions are not allowed constant expressions in C. MSVC presumably supports them as an extension, but for portable C code this should be avoided. Does MSVC have the equivalent of a `-pedantic` option? If so, it should flag this as non-compliant. – Tom Karzes Jun 27 '16 at 12:44
  • 2
    @threadp There's no need to use shifts in place of multiplication or division operators. They are perfectly valid operators in a constant expression. The problem is the floating point data type, not the divide operator. – Tom Karzes Jun 27 '16 at 12:46
  • @TomKarzes perhaps VC sees `(unsigned)(2 / 0.5)` as *floating constants that are the immediate operands of casts*. – Weather Vane Jun 27 '16 at 12:49
  • 1
    @WeatherVane: It should not. `2 / 0.5` is not a *floating-point constant*, it's a *floating-point (constant) expression*. – EOF Jun 27 '16 at 12:50
  • @TomKarzes Agree. I don't really know why I stated the shifts, but in the process of writing the comment, it seemed a good idea :). – pah Jun 27 '16 at 12:51
  • Maybe worth adding the `99)` footnote as well: 99) An integer constant expression is used to specify the size of a bit-field member of a structure, the value of an enumeration constant, _the size of an array_, or the value of a case constant. – Jahaja Jun 27 '16 at 12:54
  • 2
    @TomKarzes AFAIK the highest warning level for MSVC is `/Wall` which did not complain. I don't think there is a `pedantic` option. But as MSVC is not standard-compliant, that is hardly surprising. – Weather Vane Jun 27 '16 at 12:59
  • 1
    Nope, there's no `-pedantic` option in VC. It compiles with `-sloppy -std=none` by default. – Lundin Jun 27 '16 at 13:10
  • 1
    What version of gcc are you using? I'm getting a clean compile under 4.4.7 and an error under 4.8.3. – dbush Jun 27 '16 at 13:43
  • 2
    @EOF: why did you post this as a comment, when it could be an answer? – Serge Ballesta Jun 27 '16 at 14:06
  • @Sourav Ghosh I break down an operation into a number of equivalent steps and I store some data for each of the steps. The number of steps must be large enough as to ensure that each step is sufficiently small. The floating point number is an upper limit to the length of a change in a given step. So I basically use the upper limit of the length to determine how many steps to perform. – martinkunev Jun 27 '16 at 14:28
  • @dbush I'm using gcc 4.9.2 – martinkunev Jun 27 '16 at 14:45
  • With "size that should be known at compile time", why the `variable-length-array` tag? – chux - Reinstate Monica Jun 27 '16 at 15:08
  • As FP math is not generally available with `foo[(unsigned)(2 / 0.5)]`, and certainly `2 / 0.5` is only a sample equation and not the true equation of your tasks, care to post the true equation/constant your are trying to determine at compile time? – chux - Reinstate Monica Jun 27 '16 at 15:13
  • @chux I added the tag by mistake, sorry! The true expression after the macro expansions is `(unsigned)(2 * 12 / 0.25)` but since both generate the same error I don't think that the behavior is expression-specific with respect to the exact numbers. – martinkunev Jun 27 '16 at 15:24
  • 1
    @martinkunev EOF's comment is the answer. C requires the size of a non variable length array to be an **integral** constant and `(unsigned)(2 * 12 / 0.25)` is not according to the standard. It should be: `(2 * 12 * 4)`. You are not allowed to have an non integral value in the operations. – Serge Ballesta Jun 27 '16 at 15:38
  • @martinkunev `(2 * 12 / 0.25)` may be the true equation, yet what is the higher level meaning/definition of `2`, `12` and importunately `0.25`? – chux - Reinstate Monica Jun 27 '16 at 15:41
  • Your example, `static int foo[(unsigned)(2 / 0.5)];` clearly could be simplified to `static int foo[4];`. Presumably the expression in your actual code is more complicated. If you show us the actual expression that's causing you problems, we might be able to help you do what you're trying to do. (Simplifying examples is great, but in this case you may have simplified away the actual problem.) – Keith Thompson Jun 27 '16 at 20:03
  • @KeithThompson I think I got the answer I needed, but in case somebody is curious I have described my original problem in my question. – martinkunev Jun 29 '16 at 11:11
  • @chux I have updated the question. – martinkunev Jun 29 '16 at 11:11

2 Answers2

5

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.

EOF
  • 6,273
  • 2
  • 26
  • 50
1

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];
Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256