I want to define an Array in c in followed way:
#define MACRO1 5U
#define MACRO2 6U
int arr[MACRO1+MACRO2];
my question is: Is Array arr a dynamical defined Array in Heap or static defined Array in DATA-Segment(or BSS)?
I think, arr is a dynamic defined array. It is similar with an array allocated with malloc function. The reason is, that the mathematical addition will be done firstly in runtime. In Compilertime all macros will be only expanded as followed:
int arr[5U+6U];
I used a Compiler with c90 Standard.
Can anybody help me? Is my argumentation Right?
This:
#define MACRO1 5U
#define MACRO2 6U
int arr[MACRO1+MACRO2];
is equivalent to this:
int arr[5U+6U];
Since 5U+6U is a constant expression, it's evaluated at compile time, just as if you had written:
int arr[11U];
or, since the particular type of an array length is not part of the array's type:
int arr[11];
Since the length is a constant expression, arr is not a variable-length array (VLA), and it's legal to define it at file scope. (C90 did not have VLAs. C11 made support for them optional.)
If arr is defined inside a function without the static keyword, it has automatic storage duration; in most implementations, this means it's allocated on the "stack". Otherwise, it has static storage duration (it exists for the entire execution of the program, and it's allocated in whatever manner the implementation uses (perhaps a BSS segment or something similar). It will not be allocated in the same manner as by a call to malloc() (on the "heap").
Constant expressions are defined in section 6.6 of the current C standard (see the N1570 draft).
A constant expression may not contain assignment, decrement, increment, function-call, or comma operators, unless they're in a subexpression that's not evaluated. An integer constant expression may only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and a handful of other things.
when u do
#define MACRO1 5U
#define MACRO2 6U
int arr[MACRO1+MACRO2];
the c compiler just sees
int arr[11];
which is probably not 'dynamic' - I quote dynamic because you dont define what 'dynamic' means for you.
Where it gets allocated depends on where the statement appears in the program. Its either on the stack if its in a function or its static if its at file scope
arr is an array whose size is determined at compile time. In C terminology, it's an array that isn't a variable length array. That's because its size is a constant. The variable can be annotated as static or put at file scope, and that makes it a global variable, with a size that can be reserved statically (typically included in the BSS, if the C implementation has a BSS).
Any expression that is understood by the preprocessor is a constant. For example, you can have a preprocessor directive
#if MACRO1 + MACRO2 > 4
…
The preprocessor (which is part of the C compiler) performs the computation (looking up the values of MACRO1 and MACRO2, performing the addition and the comparison) in an early stage of the compilation.
There are also constants that the preprocessor cannot calculate, mostly resulting from sizeof: the result of sizeof is a compile-time constant unless the calculation involves the size of a variable length array. So while you cannot write #if sizeof(some_type) > 4, you can write things like
static unsigned char b[sizeof(x) * 2];
static c = sizeof(x) | 4;
The size of the array b and the value used to initialize c are both compile-time constants.
Basically, any expression that doesn't involve the value of a variable or a pointer dereference is a compile-time constant (“constant expression” in the terminology used by the C standard).
On the other hand, the value of a variable (as opposed to a macro) is not a compile-time constant, so it cannot be used where a compile-time constant is required¹.
const x = 3; /* OK */
static c = x; /* not ok in C */
¹ C++ works differently here. My answer is about C.
Related
I thought C had no more surprises for me, but this surprised me.
const int NUM_FOO = 5;
....
int foo[NUM_FOO];
==>error C2057: expected constant expression
My C++ experience has made me internally deprecate #define as much as possible. So this one was a real surprise. VS2019, compiled with /TC. I thought C99 allowed variable size arrays anyway.
Can anybody explain why the rejection occurs, since the compiler for sure knows at compile time the size of the array?
Is it not the case that C99 allows variable size arrays?
In C, this declaration:
const int NUM_FOO = 5;
doesn't make NUM_FOO a constant expression.
The thing to remember (and yes, this is a bit counterintuitive) is that const doesn't mean constant. A constant expression is, roughly, one that can be evaluated at compile time (like 2+2 or 42). The const type qualifier, even though its name is obviously derived from the English word "constant", really means "read-only".
Consider, for example, that these are a perfectly valid declarations:
const int r = rand();
const time_t now = time(NULL);
The const just means that you can't modify the value of r or now after they've been initialized. Those values clearly cannot be determined until execution time.
(C++ has different rules. It does make NUM_FOO a constant expression, and a later version of the language added constexpr for that purpose. C++ is not C.)
As for variable length arrays, yes, C added them in C99 (and made them optional in C11). But as jamesdlin's answer pointed out, VS2019 doesn't support C99 or C11.
(C++ doesn't support VLAs. This: const int NUM_FOO = 5; int foo[NUM_FOO]; is legal in both C99 and C++, but for different reasons.)
If you want to define a named constant of type int, you can use an enum:
enum { NUM_FOO = 5 };
or an old-fashioned macro (which isn't restricted to type int):
#define NUM_FOO 5
jamesdlin's answer and dbush's answer are both correct. I'm just adding a bit more context.
A variable with the const qualifier does not qualify as a constant expression.
Section 6.6p6 of the C11 standard regarding Constant Expressions states
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
Note that const qualified integer objects are not included.
This means that int foo[NUM_FOO]; is a variable length array, defined as follows from section 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; 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.
As for the error you're getting, that is because Visual Studio is not fully compliant with C99 and does not support variable length arrays.
const in C does not declare a compile-time constant. You can use an enum constant instead if you want to avoid using #define and want a symbolic name that can appear in a debugger.
C99 does support VLAs. However, VS2019 does not support C99.
On top of the existing answers which are all good, the reason a const-qualified object fundamentally can't in general be a constant expression, in the sense of "one that can be evaluated at compile time" (as mentioned in Keith's answer), is that it can have external linkage. For example, you can have in foo.c
const int NUM_FOO = 5;
and in bar.c:
extern int NUM_FOO;
...
int foo[NUM_FOO];
In this example, the value of NUM_FOO cannot be known when compiling bar.c; it is not known until you choose to link foo.o and bar.o.
C's model of "constant expression" is closely tied to properties that allow translation units (source files) to be translated (compiled) independently to a form that requires no further high-level transformations to link. This is also why you can't use addresses in constant expressions except for address constant expressions which are limited to essentially the address of an object plus a constant.
I'm dynamically calculating the size of an array. Something like:
void foo(size_t limit)
{
char buffer[limit * 14 + 1];
}
But just GCC compiler says:
error: ISO C90 forbids variable length array ‘buffer’
searching on SO I found this answer:
C99 §6.7.5.2:
If the size is an expression that is not an integer constant
expression... ...each time it is evaluated it shall have a value
greater than zero.
So, I did the re-declaration of size limit type variable to:
void foo(const size_t limit)
But it continues to give warning for me. Is this a GCC bug?
const-qualifying a variable doesn't make it a compile-time constant (see C99 6.6 §6 for the defintion of an integer constant expression), and before the introduction of variable-length arrays with C99, array sizes needed to be compile-time constants.
It's rather obvious that const-qualify a variable doesn't make it a compile-time constant, in particular in case of function parameters which won't be initialized until the function is called.
I see the following solutions to your problem:
compile your code as C99 via -std=c99 or -std=gnu99
allocate your buffer via malloc()
use alloca() if available, which is the closest you can come to variable-length arrays with C90
choose a maximum buffer size which is always used and fail if the given limit argument overflows
As a side note, even though C99 allows variable-length arrays, it's still illegal to use the value of an integer variable with static storage duration as size for an array with static storage duration, regardless of const-qualification: While there's nothing which prevents this in principle if the integer variable is initialized in the same translation unit, you'd have to special-case variables with visible defintion from those whose definition resides in a different translation unit and would either have to disallow tentative defintions or require multiple compilation passes as the initialization value of a tentatively defined variable isn't known until the whole translation unit has been parsed.
const does not introduce a constant in C but a read-only variable.
#define SIZE 16
char bla[SIZE]; // not a variable length array, SIZE is a constant
but
const int size = 16;
char bla[size]; // C99 variable length array, size is a constant
C90 doesn't allow variable length arrays. However, you can use the c99-gcc compiler to make this work.
You are compiling with c90-gcc but looking at C99 specifications.
No it is not a bug. You can't use a VLA in C90. When you declared
const size_t limit
that is not a constant expression. A constant expression would be something like a literal value 666.
Note that C differs significantly from C++ in this regard. Even a constant like this
const int i = 666;
is not a constant expression in C. This is the primary reason why constant values are typically declared with #define in C.
As written in your question, this is from C99, not C90, you need to compile it against C99 to be able to use variable length arrays.
A const qualified variable is not an integer constant expression in the sense of the standard. This has to be a literal constant, an enumeration constant, sizeof or some expression composed with these.
Switch to C99 if you may. The gcc option is -std=c99 (or gnu99 if you want gnu extension.)
This question already has answers here:
Can a const variable be used to declare the size of an array in C?
(5 answers)
Closed 5 years ago.
In the following code, const int cannot be used as an array size:
const int sz = 0;
typedef struct
{
char s[sz];
} st;
int main()
{
st obj;
strcpy(obj.s, "hello world");
printf("%s", obj.s);
return 0;
}
This is because in C const actually means read only. Quoting C FAQ 1.18 and 1.19:
The const qualifier really means ``read-only''; an object so qualified is a run-time object which cannot (normally) be assigned to. The value of a const-qualified object is therefore not a constant expression in the full sense of the term, and cannot be used for array dimensions, case labels, and the like. (C is unlike C++ in this regard.) When you need a true compile-time constant, use a preprocessor #define (or perhaps an enum).
References: ISO Sec. 6.4
H&S Secs. 7.11.2,7.11.3 pp. 226-7
There are two ways of dealing with it:
Use #define instead of const
Use enum { sz = 12 };
In C, a const-qualified variable is not a constant expression1. A constant expression is something that can be evaluated at compile time - a numeric literal like 10 or 3.14159, a string literal like "Hello", a sizeof expression, or some expression made up of the same like 10 + sizeof "Hello".
For array declarations at file scope (outside the body of any function) or as members of struct or union types, the array dimension must be a constant expression.
For auto arrays (arrays declared within the body of a function that are not static), you can use a variable or expression whose value isn't known until runtime, but only in C99 or later.
C++ is different in this regard - in that language, a const-qualified variable does count as a constant expression.
In a very simple way because the compiler must know the dimension of the array at compilation time and since you can initialize const variable at run time you can't do it. So the size of statically declared arrays must be a constant expression and a const variable is not it. For constant expression you should use either a macro (#define) or enum. That's explicitly for your case (at file scope) and if you use a minimum standard of c99.
I'm dynamically calculating the size of an array. Something like:
void foo(size_t limit)
{
char buffer[limit * 14 + 1];
}
But just GCC compiler says:
error: ISO C90 forbids variable length array ‘buffer’
searching on SO I found this answer:
C99 §6.7.5.2:
If the size is an expression that is not an integer constant
expression... ...each time it is evaluated it shall have a value
greater than zero.
So, I did the re-declaration of size limit type variable to:
void foo(const size_t limit)
But it continues to give warning for me. Is this a GCC bug?
const-qualifying a variable doesn't make it a compile-time constant (see C99 6.6 §6 for the defintion of an integer constant expression), and before the introduction of variable-length arrays with C99, array sizes needed to be compile-time constants.
It's rather obvious that const-qualify a variable doesn't make it a compile-time constant, in particular in case of function parameters which won't be initialized until the function is called.
I see the following solutions to your problem:
compile your code as C99 via -std=c99 or -std=gnu99
allocate your buffer via malloc()
use alloca() if available, which is the closest you can come to variable-length arrays with C90
choose a maximum buffer size which is always used and fail if the given limit argument overflows
As a side note, even though C99 allows variable-length arrays, it's still illegal to use the value of an integer variable with static storage duration as size for an array with static storage duration, regardless of const-qualification: While there's nothing which prevents this in principle if the integer variable is initialized in the same translation unit, you'd have to special-case variables with visible defintion from those whose definition resides in a different translation unit and would either have to disallow tentative defintions or require multiple compilation passes as the initialization value of a tentatively defined variable isn't known until the whole translation unit has been parsed.
const does not introduce a constant in C but a read-only variable.
#define SIZE 16
char bla[SIZE]; // not a variable length array, SIZE is a constant
but
const int size = 16;
char bla[size]; // C99 variable length array, size is a constant
C90 doesn't allow variable length arrays. However, you can use the c99-gcc compiler to make this work.
You are compiling with c90-gcc but looking at C99 specifications.
No it is not a bug. You can't use a VLA in C90. When you declared
const size_t limit
that is not a constant expression. A constant expression would be something like a literal value 666.
Note that C differs significantly from C++ in this regard. Even a constant like this
const int i = 666;
is not a constant expression in C. This is the primary reason why constant values are typically declared with #define in C.
As written in your question, this is from C99, not C90, you need to compile it against C99 to be able to use variable length arrays.
A const qualified variable is not an integer constant expression in the sense of the standard. This has to be a literal constant, an enumeration constant, sizeof or some expression composed with these.
Switch to C99 if you may. The gcc option is -std=c99 (or gnu99 if you want gnu extension.)
I'm a tad confused between what is and is not a Constant Expression in C, even after much Googleing. Could you provide an example of something which is, and which is not, a Constant Expression in C?
A constant expression can be evaluated at compile time. That means it has no variables in it. For example:
5 + 7 / 3
is a constant expression. Something like:
5 + someNumber / 3
is not, assuming someNumber is a variable (ie, not itself a compile-time constant).
There is another subtlety to constant expressions. There are some things that are known to the compiler, but cannot be known to the preprocessor.
For example (24*60*60) can be computed by both, but sizeof struct foo is only known to the compiler. This distinction can matter if you are trying to verify that a struct is defined to meet an externally mandated size, or that its members are mapped at externally specified offsets. (This use case often arises when coding device drivers where the struct describes device registers as layed out in memory space.)
In that instance you cannot simply say #if (sizeof(struct UART) == 12) because the preprocessor operates at a pass ahead of the compilation and simply cannot know the size of any types. It is, however, a constant expression and would be valid as an initializer for a global variable (e.g. int UARTwords = sizeof(struct UART) / sizeof(short);), or to declare the size of an array (e.g. unsigned char UARTmirror[sizeof(struct UART)];)
Nobody seems have mentioned yet another kind of constant expression: address constants. The address of an object with static storage duration is an address constant, hence you can do this kind of thing at file scope:
char x;
char *p = &x;
String literals define arrays with static storage duration, so this rule is also why you can do this at file scope:
char *s = "foobar";
Any single-valued literal is a constant expression.
3 0.0f '\n'
(String literals are weird, because they're actually arrays. Seems "hello" isn't really a constant, as it ends up having to be linked and all that, and the address and contents can change at runtime.)
Most operators (sizeof, casts, etc) applied to constants or types are constant expressions.
sizeof(char)
(byte) 15
Any expression involving only constant expressions is itself also a constant expression.
15 + 3
0.0f + 0.0f
sizeof(char)
Any expression involving function calls or non-constant expressions is usually not a constant expression.
strlen("hello")
fifteen + x
Any macro's status as a constant expression depends on what it expands to.
/* Always a constant */
#define FIFTEEN 15
/* Only constant if (x) is
#define htons(x) (( ((x) >> 8) | ((x) << 8) ) & 0xffff)
/* Never constant */
#define X_LENGTH strlen(x)
I originally had some stuff in here about const identifiers, but i tested that and apparently it doesn't apply in C. const, oddly enough, doesn't declare constants (at least, not ones "constant" enough to be used in switch statements). In C++, however, it does.
Another fun little wrinkle: in C, the value of an 'enum' is a constant, but may only be used after the declaration of the 'enum' is complete. The following, for example, is not acceptable in standard C, though it is acceptable in C++:
enum {foo=19, bar, boz=bar+5;};
It could be rewritten:
enum {foo=19, bar}; enum {boz=bar+5;};
though this would end up defining multiple different enumeration types, rather than one which holds all the values.
Also integral character constants as 'a' or '\n' are constants that the compiler recognizes as such. They have type int.