Variably modified array at file scope in C
Asked Answered
A

4

74

I have some code like this:

static int a = 6;
static int b = 3;

static int Hello[a][b] =
{
    { 1,2,3},
    { 1,2,3},
    { 1,2,3},
    { 1,2,3},
    { 1,2,3},
    { 1,2,3}
};

But when I compile it, it says error:

variably modified 'Hello' at file scope

How could this happen? And how could I fix it?

Aseptic answered 30/11, 2012 at 13:10 Comment(7)
Possible duplicate of Variably modified array at file scopeTartaric
@tstew: No, that is for Objective-CCottier
Related: "static const" vs "#define" vs "enum" and static const vs #defineCottier
@PeterMortensen (and others) - I've flagged a few questions as duplicates of this (I think they should all appear as Linked in the right-hand column). It would be great if you could browse them and close-vote the ones I'm right about.Villanueva
Note: The title is partly literal and "variably" is part of the (literal) error message. E.g., "variably modified ‘child’ at file scope"Cottier
@PeterMortensen The technically correct and formal term is Variable length array. I don't really see why we can't use the most correct term, especially if this post is to be used as canonical dupe.Etem
@Lundin: We can. I don't have any objections. It was just to explain the origin of the weird word "variably".Cottier
E
99

You can not have a static array whose size is given as a variable.

That's why constants should be #defined:

#define a 6

This way, the preprocessor will replace a with 6, making it a valid declaration.

Exact answered 30/11, 2012 at 13:14 Comment(3)
is a and b defined as int a = 6; int b = 3 instead of static int a = 6 works?Aseptic
No, this will still be a variable. Use #define. In C++ there is const that would allow const int a = 6; to work, but even const is not enough in C.Exact
an alternative to macro is using anonymous enums, which are true integer constants enum { a = 6, b = 3, };Inclose
U
11

Simple answer: A variable modified array at file scope is not possible.

Detailed:

Make it a compile time integral constant expression, since the array length must be specified at the compile time.

Like this:

#define a 6
#define b 3

Or, follow the C99 standard. and compile like for GCC.

gcc -Wall -std=c99 test.c -o test.out

The problem here is a variable-length array with providing length may not be initialized, so you are getting this error.

Simply

static int a = 6;
static int b = 3;

void any_func()
{
    int Hello [a][b]; // No need of initialization. No static array means no file scope.
}

Now use a for loop or any loop to fill the array.

For more information, just a demo:

#include <stdio.h>

static int a = 6;
int main()
{
    int Hello[a] = {1, 2, 3, 4, 5, 6}; // See here initialization of the array 'Hello'. It's in the function
                                       // Scope, but still an error
    return 0;
}

Compile

cd ~/c
clang -std=c99 vararr.c -o vararr

Output:

vararr.c:8:11: error: variable-sized object may not be initialized
int Hello[a]={1,2,3,4,5,6};
          ^
1 error generated.

If you remove static and provide initialization then it will generate the error as above.

But if you keep static as well as initialization then it will still be an error.

But if you remove the initialization and keep static, the below error will come.

error: variable length array declaration not allowed at file scope
static int Hello[a];
           ^     ~
1 error generated.

So a variable-length array declaration is not allowed at file scope, so make it function or block scope inside any function (but remember making it function scope must remove initialization)

Note: Since it's C tagged, making a and b as const won't help you, but in C++ const will work fine.

Uxoricide answered 30/11, 2012 at 13:17 Comment(12)
C99 doesn't support VLA's at file-scope either. it must be at function-scope or smaller. He 'can' use const index declarations, including static const int a = 10; for example, at file scope.Standardbearer
Its a stack-thing. If they're even supported by your C99 compiler, it isn't mandated that they be; the standard defines how they behave if your C99 does support them. The implementations use stack-math to implement them. You can't do that at global (or file) data-segment compilation level.Standardbearer
@WhozCraig: But when I define a in main in that case also it's not working, compiling with -std=c99 . now a is on stack so what's the problem now , I have tried with gcc and clangUxoricide
@WhozCraig: I read somewhere on SO itself so posted the answer , but I am now myself facing this with -std=c99 and not compiling, any clue ? going to edit my answer alsoUxoricide
@Standardbearer : I got it ,YOu are wrong about stack- thing it could be globalUxoricide
Please show me how, as I've tried in the past to have VLA's globally declared and every compiler I've ever used puked on them. Its definitely worth an up-vote if you would, please.Standardbearer
@Omkant, you could add that the other kind of integral constant expressions that could be used here are enumeration constants.Lakes
@Standardbearer : see my answer , and tell me agreed or not , ideone.com/f1AykV for correct , ideone.com/VV12D5 , for error.It's just a demo I did.Uxoricide
@Omkant, you really worsened your answer with your edit. Neither the [][] nor with static int Hello[a][b] are valid C.Lakes
what confuses me is why static size_t const size wouldn't work for the size of an array at file scope. It works in function scope even in C89, so I would expect an object that is constant and is known at compile and load time to be able to be used there. Unless static is the problem, which sucks because I would like to keep them tied to the one compilation module.Psychrometer
actually, this only happens if the array is a static uint8_t[]. The compiler has no problem doing it for static char[]. But then, when I try to set up a repl.it to demonstrate this (mind you, this was after having already demonstrated the error in an anonymous repl.it and copy/pasting the exact code from that repl.it into a named repl.it), it just compiles without issue.Psychrometer
I ran into this issue while I was writing a brainfuck VM in C, while creating the physical memory and assigning pointers into it for the program counter and virtual memory.Psychrometer
J
4

When using Clang/LLVM, the following works:

static const int a = 6;
static const int b = 3;

static int Hello[a][b] =
{
    {1, 2, 3},
    {1, 2, 3},
    {1, 2, 3},
    {1, 2, 3},
    {1, 2, 3},
    {1, 2, 3}
};

(To see it in the generated assembly, one needs to use 'Hello', so it will not be optimized out.)

However, this will generate an error if C99 mode is selected (-std=c99). It will only generate a warning (Wgnu-folding-constant) if -pedantic is selected.

=== EDIT === Clang now (version 14.0.0) generates this warning as default: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]

GCC does not seem to allow this (const is interpreted as read-only).

See the explanation in this question:

"Initializer element is not constant" error for no reason in Linux GCC, compiling C

Jacki answered 2/4, 2015 at 4:48 Comment(5)
The 'const' keyword in C does not really mean 'constant'. This misleads some users.Curie
@Lowpower I agree - this is a detail of the implementation. Moreover the actual implementation of cost seems to vary, in C++ at least some const are no longer 'read only variable (meaning there is no symbol in the symbol table pointing to read-only memory segment) but true constants resolved at compile-time.Jacki
Neither clang nor gcc allows this to compile cleanly, because it is invalid C and a non-standard compiler extension. Notably a program could "just have warnings" and still be invalid C.Etem
@Etem This apparently changed (which is good) since I wrote the answer, Clang now generates the warning: "variable length array folded to constant array as an extension" (-Wgnu-folding-constant) by default. The code is still "valid" - if that means accepted by the compiler and works s expected (like all extensions) but is not standard.Jacki
If something goes against a constraint or syntax rule in the standard, then it is invalid C - it is not C but something else - "C with extensions". Compiler extensions might often violate constraints/syntax, but doing so will leave the compiler non-conforming unless it gives a warning.Etem
M
0

The dimensions of the array must be constant expressions and your friend 'compiler' must be informed about that. So tell to compiler that a and b are constant values.

static constexpr int a = 6;

static constexpr int b = 3;

static int Hello[a][b] = { { 1,2,3 }, { 1,2,3 }, { 1,2,3 }, { 1,2,3 }, { 1,2,3 }, { 1,2,3 } };
Modica answered 25/8, 2023 at 10:57 Comment(1)
Use 'const' for older C/ C++Modica

© 2022 - 2024 — McMap. All rights reserved.