const values in array dimensions [duplicate] - c

This question already has answers here:
variably modified array at file scope in C
(4 answers)
Closed 5 years ago.
The below code snippet works and compiles in C
const int n=10;
int main(void)
{
int a[n];
return 0;
}
However when array is declared in global scope ,it throws compilation error.
const int n=10;
int a[n];
int main(void)
{
return 0;
}
Can someone please explain why it doesn't throw error inside main.

Despite n being marked as const, it's not a compile time constant1. As such the array a is a variable length array2.
VLA's cannot have static storage duration. Placing it at file scope makes it a static variable, and your program is ill-formed because of it3.
C11 N1570/6.6p6
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.
C11 N1570/6.7.6.2p4
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.
C11 N1570/6.7.6.2p2
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.

Related

Defining a global constant [duplicate]

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.

Why can't I use a constant int to declare an array AND initialize it in C?

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.

Can array length in declaration be non-constant?

I am a bit confused about array declaration in C. I know that it's possible to do this:
int a[20]; // Reserved space for 20 int array
int b[] = {32, 431, 10, 42}; // Length in square brackets is auto-calculated
int *c = calloc(15, sizeof(int)); // Created a pointer to the dynamic int array
But is it possible to do this?:
int my_array[sizeof(int) * 5];
Is it a valid code, or an array length should be a constant expression (in ANSI C)?
This declaration
int my_array[sizeof(int) * 5];
does not declare a variable length array because the expression sizeof(int) * 5 is a constant integer expression. So even your compiler does not support variable length arrays you may use such a declaration.
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.)
A declaration of a variable length array can look like
const int n = 5;
int my_array[sizeof(int) * n];
The support of variable length arrays is optional in C11 and higher.
(This answer answers the question in the title, “Can array length in declaration be non-constant?” The example given in the body, int my_array[sizeof(int) * 5]; does not have a non-constant length.)
Variable length arrays are optional in the current C standard, 2018, meaning a C implementation may choose to support them or not. They were mandatory in the 1999 C standard and made optional in the 2011 standard.
Variable length arrays can be declared only inside functions or there parameters, not at file scope, and they cannot have static or thread storage duration.
sizeof(int) * 5 used in the example statement in your question: int my_array[sizeof(int) * 5];, is a constant expression, so although it does not serve as a good illustration of your primary question, it is legal syntax for C array declaration.
With the exception of C99, variable length arrays are optional in most recent C compiler implementations. (In C99 inclusion of VLA is mandated.)
So, if your compiler supports VLA, the following are an examples:
char string[100] = {0};
scanf("%99s", string);
int VLAarray1[strlen(string)+1];//per question in comments about functions to size array.
memset(VLA1array, 0, sizeof(VLAarray1));//see Note below for initialization
int arrayLen = 0;
scanf("%d", &arrayLen);
int VLAarray2[arrayLen];
memset(VLAarray2, 0, sizeof(VLAarray2));//see Note below for initialization
int nonVLAarray[100] = {0};//initialization during declaration of nonVLA
Note: that VLAs cannot be initialized in any form during its declaration. As with all variables though it is a good idea that it be initialized in subsequent statements by explicitly assigning values to its entire region of memory.
Passing VLAs as function arguments is not included within the scope of your question, but should it be of interest, there is a good discussion on that topic here.

array bound is not an integer constant before ']' token when it is actually constant

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];

why cant we assign a value to global variables using an expression in C ?

In the below code snippet , I am trying to assign a value to a global variable using an expression but it gives the error "initializer element is not constant" ,I am unable to get the logic behind this ,please clarify this .
#include<stdio.h>
int a=8;
int b=a+9;
int main()
{
printf("%d",b);
return 0;
}
The reason is that global variables are initialized in compiling time,
and you are trying to initialize b=a+9; as an operation that must be done in execution time.
You can assign a global variable using an expression. But only inside a function. As said by the compiler/linker, outside a function you can initialize variables only to constant. This is because, outside a function, no code can be executed and the variables are initialized to constant value by the compiler/linker at build time. Note that if you don't init the variable (outside a function), the default initialization is to 0.
To make it work you should write
#include<stdio.h>
int a=8;
int b;
int main()
{
b=a+9;
printf("%d",b);
return 0;
}
As b is of arithmetic type and has static storage duration, its initializer has to satisfy the rules of arithmetic constant expressions (C11 §6.6 Expressions):
8 An arithmetic constant expression shall have arithmetic type and
shall only have
operands that are integer constants, floating constants, enumeration constants, character
constants, sizeof expressions whose results are integer constants, and _Alignof
expressions.
Cast operators in an arithmetic constant expression shall only convert
arithmetic types to arithmetic types, except as part of an operand to a sizeof or
_Alignof operator.
int b=a+9; doesn't.

Resources