I'd like to initialize an array based on a calculation, but the compiler gives me an error when I try this (I'm using GCC version 6.3.0):
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
char textgrid[SCREEN_HEIGHT/16][SCREEN_WIDTH/16];
The compiler error is as follows:
error: variably modified 'textgrid' at file scope
Is there a way to do this at file scope?
It seems I can't use calculations as part of a #define statement to accomplish this, because the following gives me the same error:
#define TEXTGRID_WIDTH (SCREEN_WIDTH / 16)
#define TEXTGRID_HEIGHT (SCREEN_HEIGHT / 16)
char textgrid[TEXTGRID_HEIGHT][TEXTGRID_WIDTH];
According to the C Standard (6.7.6.2 Array declarators)
... 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.
and (6.7.6.2 Array declarators)
2 If an identifier is declared as having a variably modified type, it
shall be an ordinary identifier (as defined in 6.2.3), have no
linkage, and have either block scope or function prototype scope. If
an identifier is declared to be an object with static or thread
storage duration, it shall not have a variable length array type.
and at last (6.6 Constant expressions)
6 An integer constant expression 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.
So you can use either defined named constants like
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
or enumerators
enum { SCREEN_WIDTH = 800, SCREEN_HEIGHT = 600 };
When you write const int SCREEN_WIDTH = 800, you define an object named SCREEN_WIDTH that contains the value 800.
Because SCREEN_WIDTH is an object, not a value, you cannot use it for a value in a constant expression. Even though its value is unchanging and obvious to us, it does not qualify as a compile-time constant.
To define an array at file scope, you must use dimensions that are constant expressions (expressions formed entirely of compile-time constants).
In C, a const qualified variable is not considered a compile time constant. You'd be needing a compile time constant to mention the array dimension in file scope.
You can make use of #define statements for this purpose.
Related
This question already has answers here:
Can a const variable be used to declare the size of an array in C?
(5 answers)
Closed 1 year ago.
#include <stdio.h>
const int TAILLE_MAX = 10;
int iTableau[TAILLE_MAX]={3,6,0,8,97,0,5,6,0,8};
const int TAILLE_MAX = 10; is not a compile-time expression in C, so it cannot be used as a compile-time array size. Replace the expression with a macro: #define TAILLE_MAX 10
See also this StackOverflow question.
const int something = 100;
does not make the something a constant expression. It is constant variable but not constant expression.
Static storage duration objects sizes and initializers have to be constant expressions
An integer constant expression* 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.
(*)An integer constant expression is required in a number of
contexts such as the size of a bit-field member of a
structure, the value of an enumeration constant, and the size
of a non-variable length array. Further constraints that apply to
the integer constant expressions used in conditional-inclusion
preprocessing directives are discussed in 6.10.1
In your case change it to:
#define TAILLE_MAX 10
This question already has answers here:
Variably modified array at file scope
(6 answers)
Closed 2 years ago.
Why does the following work:
#define MAX 100
char MY_ARRAY[MAX];
But the following does not:
int MAX_2 = 200;
char MY_ARRAY_2[MAX_2];
Here is an example from Compiler Explorer. The interesting thing to me is before defining the final char array things look like it should work -- all integers are defined in data...
In the declaration of the array
#define MAX_VALUE 100
char MY_ARRAY[MAX_VALUE];
there is used an integer constant expression for the size of the array.
In this declaration
int MAX_VALUE=100;
char MY_ARRAY[MAX_VALUE];
there is declared a variable length array. But you may not declare a variable length array with the static storage duration (in particularly in a file scope). You could declare such an array in a block scope if the compiler supports variable length arrays.
Even if you will use the qualifier const in the declaration of the variable MAX_VALUE like
const int MAX_VALUE = 100;
it will not produce an integer constant expression according to its definition in the C Standard.
Instead you could write
enum { MAX_VALUE = 100 };
char MY_ARRAY[MAX_VALUE];
From the C Standard (6.6 Constant expressions)
6 An integer constant expression117) 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.
And (6.7.6.2 Array declarators)
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 or type names with function prototype scope; 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. (Variable length arrays
are a conditional feature that implementations need not support; see
6.10.8.3.)
and
2 If an identifier is declared as having a variably modified type, it
shall be an ordinary identifier (as defined in 6.2.3), have no
linkage, and have either block scope or function prototype scope. If
an identifier is declared to be an object with static or thread
storage duration, it shall not have a variable length array type.
Option 1: use #define
#define kSize 5
int arr1[kSize] = {1,2,3,4,5};
--> OK.
Option 2: use an enum
enum { eSize = 5 };
int arr2[eSize] = {1,2,3,4,5};
--> OK.
But, a const int cannot be used:
const int cSize=5;
int arr3[cSize] = {1,2,3,4,5};
--> FAIL.
why?
A variable with the const qualifier does not qualify as an integer constant expression. This makes the array a variable length array (VLA) which cannot be initialized.
Section 6.7.6.2p4 of the C standard describing Array Declarators states:
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 or type names with function prototype
scope 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. (Variable length arrays are a conditional feature that
implementations need not support; see 6.10.8.3.)
So for an array to not be a variable length array its size must be an integer constant expression. This is defined in section 6.6p6:
An integer constant expression shall have integer type and shall
only have operands that are integer constants, enumeration
constants, character constants, sizeof expressions whose
results are integer constants, _Alignof expressions, 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 or _Alignof .operator
A #define definition is replaced by the preprocessor before the compilation phase, so in your first case kSize is exactly the same as the constant 5. The above passage also states than an enum constant qualifies as an integer constant expression, so this makes your second case OK. The third case uses a const qualified variable which is not included above in the definition of an integer constant expression, so this makes it a variable length array.
Section 6.7.9p3 then dictates what can be initialized:
The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.
And as stated above a VLA cannot be initialized.
I am trying to have an array, which has a defined size known during compile time.
const uint8_t a[2] = {0, 127}; // Fine
const uint8_t aRange = a[1] - a[0]; // Fine
double sums[aRange]; //Fails
But this fails by gcc with
error: array bound is not an integer constant before ']' token.
As a workaround I intend to use macro variables. But would like to know if there is any logic behind it. There is this answer, which is most related. However, according to the answer, it should have worked.
aRange is a constant integer, but not an integer constant. Isn't English a fun language?
C11 §6.4.4.1 Integer constants
C11 §6.6 Constant expressions ¶6
An integer constant expression117) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, 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 or _Alignof operator.
117) An integer constant expression is required in a number of contexts such as the size of a bit-field member of a structure, the value of an enumeration constant, and the size of a non-variable length array. Further constraints that apply to the integer constant expressions used in conditional-inclusion preprocessing directives are discussed in 6.10.1.
C11 §6.7.6.2 Array declarators — one of the more inscrutable parts of the standard. (¶2 is a constraint; ¶4 and ¶5 specify the semantics of array declarators.)
¶2 If an identifier is declared as having a variably modified type, it shall be an ordinary identifier (as defined in 6.2.3), have no linkage, and have either block scope or function prototype scope. If an identifier is declared to be an object with static or thread 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 or type names with function prototype scope;143) 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. (Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.)
143) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3).
¶5 If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero. The size of each instance of a variable length array type does not change during its lifetime. Where a size expression is part of the operand of a sizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.
At file (global) scope, you must have an integer constant expression for the dimensions of an array. In a local variable in C99 or later, what you've written would be OK for a VLA (variable-length array).
You could work around this with:
enum { A_MIN = 0, A_MAX = 127 };
const uint8_t a[2] = { A_MIN, A_MAX };
const uint8_t aRange = a[1] - a[0];
double sums[A_MAX - A_MIN];
In C, you can't write const uint8_t aRange = a[1] - a[0]; at file (global) scope, so your code should have been OK unless you're using an antiquated C compiler that doesn't recognize C99 or later (or it defines __STDC_NO_VLA__).
Jonathan's answer is accepted. As a workaround I used macros though.
#define A_MIN 0
#define A_MAX 127
const uint8_t a[2] = {A_MIN, A_MAX};
const uint8_t aRange = A_MAX - A_MIN;
double sums[aRange];
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.
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.
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];