I just did a experiment yesterday, and find something confusing:
#include <stdio.h>
int main()
{
int j;
scanf("%d",&j);
const int i = j;
int arr[i];
return 0;
}
The number j is read from keyboard and it’s used to allocate the array arr on the stack.
The compiler does not even know the size of the array at compile time (initializes j to 0?), but there is no compilation error. How is it possible?
Variable length arrays were added to C99. It's described in the C99 rationale:
6.7.5.2 Array declarators
C99 adds a new array type called a variable length array type. The
inability to declare arrays whose size is known only at execution time
was often cited as a primary deterrent to using C as a numerical
computing language. Adoption of some standard notion of execution time
arrays was considered crucial for C’s acceptance in the numerical
computing world.
The number of elements specified in the declaration of a variable
length array type is a runtime expression. Before C99, this size
expression was required to be an integer constant expression.
There is no "dynamic array allocation on the stack". The array size has to be specified at the declaration.
Some compilers, like GCC allow them as an extension in C90 (in GCC, this is equivalent to ansi and C89) mode and C++. In these cases, you will get a warning (-Wpedantic) or an error (-Werror or -pedantic-errors). Consult the documentation for your compiler.
Per #Deduplicator's comment, you seem to have a misconception. Variable length arrays cannot be declared static.
§ 6.7.6.2
10 EXAMPLE 4 All declarations of variably modified (VM)
types have to be at either block scope or function prototype scope.
Array objects declared with the _Thread_local, static, or extern
storage-class specifier cannot have a variable length array (VLA)
type. However, an object declared with the static storage-class
specifier can have a VM type (that is, a pointer to a VLA type).
Finally, all identifiers declared with a VM type have to be ordinary
identifiers and cannot, therefore, be members ostructures or unions.
This means that static storage and automatic storage are mutually exclusive.
For some delving into how allocating a variable amount of memory on the stack can work, see this delving into how a compiler can implement the (non-standardized) alloca() function:
Alloca implementation
The C99 standard offers Variable Length Arrays ("VLA") with essentially the same functionality; although the memory is reclaimed on a per-scope basis rather than a per-function basis:
What's the difference between alloca(n) and char x[n]?
There are some reasons to be hesitant to use either too aggressively with unbounded size. There's no way to check if stack memory is available as you can test for whether heap memory is available via. NULL from malloc(). If your variable length array is too large it will cause a stack overflow and undefined behavior; true for both methods of stack allocation:
Why is the use of alloca() not considered good practice?
C has such a feature as Variable Length Arrays. Arrays with automatic storage duration can be defined in the flight.
Related
I have some concepts about the VLA and its behavior that I need to clarify.
AFIK since C99 it's possible to declare VLA into local scopes:
int main(int argc, char **argv)
{
// function 'main' scope
int size = 100;
int array[size];
return 0;
}
But it is forbidden in global scopes:
const int global_size = 100;
int global_array[global_size]; // forbidden in C99, allowed in C++
int main(int argc, char **argv)
{
int local_size = 100;
int local_array[local_size];
return 0;
}
The code above declares a VLA in C99 because the const modifier doesn't create a compile-time value. In C++ global_size is a compile-time value so, global_array doesn't become a VLA.
What I need to know is: Is my reasoning correct? Is the behaviour that I've described correct?
I also want to know: Why are the VLA not allowed in global scope? are they forbidden both in C and C++? Why is the behavior of arrays into global and local scope different?
Yes your reasoning is correct, that is how these different forms of array declarations and definitions are viewed by C and C++.
As others already stated, VLA with a veritable variable length (non-const) in global scope is difficult to make sense. What would the evaluation order be, e.g if the the length expression would refer to an object of a different compilation unit? C++ doesn't have VLA, but it has dynamic initialization of objects at file scope. And already this gives you quite a head ache, if you have to rely on evaluation order.
This leaves the small gap for C concerning length expressions that contain a const qualified object, which isn't allowed. This comes from the fact that such objects are not considered "integer constant expressions" by the C standard. This could perhaps change in future versions, but up to now the C committee didn't find it necessary to allow for such a thing: there are enum constants that play that role in C. Their only limitation is that they are limited to int in C, it would be nice to also have them size_t.
C++ doesn't support VLAs, period. The reason the second code snippet works in C++ is that the const keyword creates a compile-time constant in C++; in C, it doesn't.
C99 doesn't support VLAs outside of block scope, period, regardless of how you declare the size variable. Note that C2011 makes VLA support optional.
There is a difference between being forbidden and not being allowed. ;-)
The VLA feature is designed to allow the use of stack space for a local array, to avoid the use of malloc for heap allocation. It is mostly a speed optimization.
Now you want to use VLAs outside of functions. Why? There is not much to win speedwise in avoiding a single malloc call during program start. And what stack space are we supposed to use for variables with a static life time?
I think the fundamental reason is a global variable has linkage, its size has to be known at compile time. If not, how could one link the program?
local variables have no linkage and the VLAs are allocated on the stack, which grows dynamically as the program runs.
So, for global VLA's, one of the issues (there are many variants on the theme) can be shown here:
int size;
int a;
int v[size];
int b;
....
in another file:
extern int a;
extern int b;
The linker will have to know where a and be are in relation to each other at link time, or it won't be able to fix them up correctly at load-time.
Can I use a variable to define an array's size?
int test = 12;
int testarr[test];
Would this work, I don't want to change the size of the array after initialization. The int test's value isn't known at compile time.
From C99 it is allowed but only for the automatic variables.
this is illegal:
int test = 12;
int testarr[test]; // illegal - static storage variable
int foo(void)
{
int test = 12;
static int testarr[test]; // illegal - static storage variable
}
the only valid form is:
int foo(void)
{
int test = 12;
int testarr[test]; // legal - automatic storage variable
}
can i use an variable to define an arrays size?
This is called Variable Length Arrays (VLA).
Read Modern C then the C11 standard n1570 (and see this reference). VLAs are permitted in §6.7.6; also read the documentation of your C compiler (e.g. GCC).
But you don't want to overflow your call stack, typically limited to a megabyte on laptops (and OS specific).
So you may prefer C dynamic memory allocation with e.g. malloc(3) (and free ...) or calloc.
Beware of memory leaks.
You might be interested by tools such as valgrind. You need to learn to use your debugger (e.g. GDB).
Can I use an integer variable to define an arrays length?
Yes, that is what is called a variable length array and is part of the C standard since C99. Note that an implementation does not need to support it. Therefore you may prefer dynamic memory allocation. Take a look at here:
malloced array VS. variable-length-array
To cite the C standard:
"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;146) 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.)"
"146) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3)."
Source: C18, 6.7.6.2/4
Also note:
"Array objects declared with the _Thread_local, static, or extern storage-class specifier cannot have a variable length array (VLA) type."
Source: C18, 6.7.6.2/10
VLAs cannot be used:
at file scope.
when qualified with _Thread_local, static, or extern storage-class specifier.
if they have linkage.
Which means that they can only be used at function-scope and when of storage-class automatic, which is done by default when omitting any explicit specifier.
Feel free to ask for further clarification if you don't understand something.
Without knowing much of the context, wouldn't it be easier to just do this?
#define TEST 12 //to ensure this value will not change at all
int testarr[TEST];
Technically your method should work too but the value of test may change later on depending on the piece of code you written
So, when you try this on C, as in this [https://onlinegdb.com/rJzE8yzC8][1]
It successfully compiles and you can also update the value of the variable int test
However, after the update of test, the size of array does not change since arrays are static-defined.
For guarantee, I suggest you to use either const int or macro variables for doing this.
Is the following legal?
const int n=10;
static int array[n];
If, yes, then why and how?
Note that in C language const objects do not qualify as constants. They cannot be used to build constant expressions. In your code sample n is not a constant in the terminology of C language. Expression n is not an integral constant expression in C.
(See "static const" vs "#define" vs "enum" and Why doesn't this C program compile? What is wrong with this? for more detail.)
This immediately means that your declaration of array is an attempt to declare a variable-length array. Variable length arrays are only allowed as automatic (local) objects. Once you declare your array with static storage duration, the size must be an integral constant expression, i.e. a compile-time constant. Your n does not qualify as such. The declaration is not legal.
This is the reason why in C language we predominantly use #define and/or enum to introduce named constants, but not const objects.
const int n=10;
static int array[n];
This code will encounter an error :
storage size of ‘array’ isn’t constant static int array[n];
^
Static memory allocation refers to the process of reserving memory at compile-time before the associated program is executed, unlike dynamic memory allocation or automatic memory allocation where memory is allocated as required at run-time.
const in C donot make that variable available in compile-time.
Statement like this would not generate that error:
static int array[10];
So, the statement that you have written is illegal or it encounter error while compiling the program.
static vars must ve allocated in COMPILE time, and thus their size and initialization value if any MUST be known at compile time. One could argue that using compile time optimizations the n var would/could be replaced with the constant value 10, and thus it might be possible to successfully compile that specific case.
When I tried this code it works:
const int i = 5;
int main() {
int arry[i];
}
Even though this didn't work:
const int i = 5;
int arry[i];
int main() {
}
I have read all posts here about arrays with constant sizes, but I can't understand why when declaring arry in main it works.
The issue here is that const in C doesn’t result in a true constant.
When you write const int i = 5 what you have is a read-only variable and not a constant. In C99 an array dimensioned with i is a variable length array (VLA). VLAs are only available for stack allocated variables, hence the compilation error you see.
If you need an array with global scope you should switch to a macro.
#define ARRAY_SIZE 5
int arry[ARRAY_SIZE];
This is valid because 5 is a literal which is a true constant.
In fact, even for an array of automatic storage (i.e. stack allocated local variable) you should avoid a VLA since they incur a runtime overhead.
Version 2 is simply not valid C, since i is a read-only variable rather than a true compile-time constant. If you wanted to make it work, you could use malloc and free (or a #define for the size).
Version 1 uses a variable-length array, which is standard feature of C99. gcc supports this syntax in pre-C99 code as an extension. If you compiled your code as C90 and turned on -pedantic, you'd get a warning that ISO C90 forbids variable length array ‘arry’.
why can we do this in c?
int n;
scanf("%d",&n);
int a[n];
I thought array is located memory during load time but seems like the above example works during runtime.
Do I misunderstand any thing? can you guys help?
Thanks,
I am no expert in C, but this could be a variable-length array as added by C99 and supported by GCC, for example. GCC allocates the memory for such array on stack, so that it gets automatically freed when you return from the function.
Variable-length arrays are not found in C89, but are a new feature in C99.
I thought array is *al*located memory during load time but seems like the above example works during run-time.
Yes, ordinary arrays like <datatype> <Array_Name> [<size>] is allocated memory during load time it is there in C89 and also existed in C99.
But in the code snippet int a[n]; is a Variable Length Array or VLA for short.VLA's in C99 are defined just like any other array, except that the length doesn’t need to be a compile-time constant.
A decent article on the need of VLAs can be found here :http://www.ddj.com/cpp/184401444 :)
Given how your code is written (specifically, that you have a statement), this must be code within a function.
While I'm not sure if this is strictly required in the spec, within a function, all auto (i.e. function level, not static) arrays are put on the stack. So regardless of whether you have a regular or VL array, the memory is allocated at runtime.
The memory for non-auto arrays is not handled at runtime so do no support VLA. If you try to compile the following code:
extern int size;
char buff1[size];
void doit(int x)
{
static int buff2[x];
int buff3[x];
}
On the compiler I tested this on (gcc 4.2.1), I got following errors:
moo.c:2: error: variably modified ‘buff1’ at file scope
moo.c: In function ‘doit’:
moo.c:6: error: storage size of ‘buff2’ isn’t constant