Can a const variable be used to declare the size of an array in C?
Asked Answered
O

5

66

Why does the following code throw an error?

const int a = 5;
int b[a] = {1, 2, 3, 4, 5};

And also when I tried to compile the above code without "const" keyword, I got the same error:

int a = 5;
int b[a] = {1, 2, 3, 4, 5};

Why is it so? What is the mistake that I am doing here?

And also another question: When are constants replaced with their actual values in a code, i.e., if I declare a variable say: const int x= 5;

I know that no memory is allocated in RAM for the variable x, but constant variable area in ROM holds the value 5 and that x is simply replaced by the value 5 everywhere x appears in the code. But when does this happen? Compilation time? Boot up time? Preprocessing time?

PS: I am talking about embedded C (running on a microcontroller, etc.), not C running on my desktop. So the embedded system is bound to have a ROM (flash, EEPROM, etc.). What would happen then?

Oloughlin answered 17/9, 2013 at 11:23 Comment(10)
Compilation time. btw ROM is like dvd's and such, there is no built in ROM in a PC (when would it be populated anyway?)Aceae
@Vprimachenko: OT: Couldn't the BIOS be considered ROM? At least there where days you had to pull it out of its socket to re-write it.Wiatt
You could use an enum instead of a const.Nonjuror
@Wiatt you're right, didn't thought of thatAceae
This would be a better question if it avoided the anti-pattern of redundantly specifying a size both as the length of the initializer list and as a value in []. In this case just leave the [] empty and do static const size_t size = sizeof(arr)/sizeof(arr[0]); after the array declaration. This is valid at global scope. A better example would be arr[size] = {} to declare a zero-initialized array of some size.Portecochere
How about putting this in a module header: extern const size_t myArraySize; And in the module itself declaring an array with the [] empty as @PeterCordes suggests, and then defining a non-static myArraySize (again via dividing sizeof array with sizeof first element ) ? I just tried it and it seems to work, but I'm not sure if it's safe given C's famous initialisation ordering indeterminacy in certain situations..Brawl
@Razzle: link-time optimization would let all compilation units see that extern const size_t myArraySize; as a compile-time constant value. Re: initialization order: maybe you're thinking of C++'s runtime initialization order problem? I don't think C has that problem, and certainly not here: the definition of myArraySize will have a compile-time constant initializer, so all references to it from other compilation units are just referencing the value in static read-only memory. (If you don't use link-time optimization to let them inline the value.)Portecochere
Possible duplicate: Variably modified array at file scope in C.Benilda
Does this answer your question? Variably modified array at file scope in CVial
VLA = variable-length arrayBenilda
F
53

It's simply a limitation of the language. The sizes of statically-bounded arrays need to be constant expressions, and unfortunately in C that's only something like a literal constant or a sizeof expression or such like, but not a const-typed variable.

(As Simon pointed out, since C99 there are also runtime-bounded arrays, or "variable-length arrays", whose size can be given by the value of any variable. But that's a different animal.)

You may be interested to hear that the rules are different in C++, where a static const int is indeed a constant expression, and C++11 even adds a new keyword, constexpr, to allow even more general use of constant expressions which encompass more things whose value "could reasonably be determined at compile time".

Fascine answered 17/9, 2013 at 11:25 Comment(3)
The rules are different in C99 as well, though the array will be considered to be dynamic.Nonjuror
@SimonElliott: What are the rules for C99? Or more importantly, for C11? (Or were you just referring to VLAs?)Fascine
:I was just on about VLAsNonjuror
R
51

In C, const is a misnomer for read-only. const variables can change their value, e.g. it is perfectly okay to declare

const volatile int timer_tick_register; /* A CPU register. */

which you can read and get a different value with each read, but not write to. The language specification thus treats const qualified objects not as constant expressions suitable for array sizes.

Roop answered 17/9, 2013 at 13:25 Comment(1)
But you can have volatile const in C++, and there it is allowed to use const as array size...Caber
N
41

2 major alternatives to VLA: enum and macros

With an anonymous enum:

enum { N = 5 };
int is[N];

as in:

#include <stdio.h>

enum { N = 5 };
char is[N];

int main(void) {
    printf("%ju\n", sizeof(is));
}

This works because enum members are constant expressions: Can enum member be the size of an array in ANSI-C?

With macros:

#define N 5
int is[N];

The advantage of enums is that enums have scope, and are part of the compilation step, so they may lead to better error messages as well.

The advantage of macros is that you have more control over the type of the constants (e.g. #define N 1 vs #define N 1u), while enums are fixed to some implementation defined type: Is the sizeof(enum) == sizeof(int), always? But it doesn't matter much in this case.

Tested on Ubuntu 21.04, GCC 10.3.0, gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c.

Why avoid VLA

Numismatics answered 18/5, 2015 at 21:5 Comment(5)
"But you should avoid using variable length arrays because they may incur an overhead." - what overhead?Anorak
@MattMcNabb updated with link to #2035212 in which top answers say there may be overheadNumismatics
This answer is the only one with a solution (enum instead of #define - yuck).Pneumothorax
@Pneumothorax yes, the endless joys of C. But I'm "confident" constexpr will make it in C2x :-)Numismatics
you could use untagged enum to avoid polluting the namespace : enum { N = 5 };Sharpie
S
4

I just read on Wikipedia that C11 has relegated variable-length arrays to an optional feature :( Doh! The first half of the post may not be that useful, but the second half answers some of your other questions :)

As an extra to Kerrek SB's post, C99 (ISO/IEC 9899:1999) does have the concept of a variable-length array. The standard gives the following example:

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // Variable-length array
    return sizeof b; // Execution time sizeof
}

The sizeof operator is extended as follows:

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

Another nice example can be found on Wikipedia.

Note that statically declared cannot be variable-length arrays.

As for some of your other questions:

Q: When are constants replaced with their actual values in a code?

If the constant is a const variable then it may never be "replaced" and could always be accessed as an area of memory. This is because the address-of operator & still has to work with the variable. However, if the variable address is never used then it can be "replaced" and have no memory allocated. From the C standard:

The implementation may place a const object that is not volatile in a read-only region of storage. Moreover, the implementation need not allocate storage for such an object if its address is never used.

Next question...

Q: I know that no memory is allocated in RAM for the variable x, but constant variable area in ROM holds the value 5

This depends on your system. If you have ROM and the compiler knows where ROM is located then it may well be placed in ROM. If there isn't any ROM, the only choice the compiler (well, the linker really) will have is RAM.

Q: x is simply replaced by the value 5 everywhere x appears in the code. But when does this happen? Compilation time? Boot up time? Preprocessing time?

As noted, this rather depends on how the constant is used. If the address of the const variable is never used and the compiler is clever enough, then at compilation time. Otherwise, the "replacement" never occurs, and it is a value with a location in memory; in this case the placement of the variable in memory happens at link time. It will never occur during preprocessing.

Spaniel answered 17/9, 2013 at 11:33 Comment(2)
Thanx for your answer! Your answers are great! But its my mistake. I forgot to mention, I am talking about Embedded C, code running on an embedded system. So it is bound to have a ROM (Flash, EEPROM) etc. What would happen in that case?Oloughlin
If you take the address of the const variable at any point the compiler/linker will have to create a memory location for the variable. At compile time the symbol is created. At link time the linker will figure out where to store the variable. This will be determined by some kind of linker directive file. If the const variable never has it's address taken then, assuming the compiler is smart enough, it could optimize it out and not allocate any storage for it, it which case the "replacement" so to speak happens during compile timeSpaniel
U
2

Maybe it is worth to mention, here you could use

int b[] = {1, 4, 5};

In case you will need the number of elements

 size_t sz = sizeof(b)/sizeof(b[0]);

I believe it is up to the toolchain, to decide where to store constants, in flash or RAM.

Unnerve answered 5/7, 2018 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.