Array sizes and const

L

Les Cargill

I am using Mingw 3.4.5 on a 'Doze box, but it pretty much
follows any Gnu compiler I've run into.

Why is this:

---------------------------
#define SIZE 20
const int size = SIZE;
char array[SIZE];
---------------------------

legal, yet

---------------------------
#define SIZE 20
const int size = SIZE;
char array[size];
---------------------------

not?

The int size is invariant, and ... "morally"
equivalent to using SIZE, yet it won't let me do that.

And yes, I understand/speak/grok preprocessor vs.
compiler. I know *how*, I just wonder *why*. Is
this a practical, strategic or tactical thing,
or am I just being silly? Was it a conscious decision
of the committee, or just one of those things?

Thanks in a advance for any consideration.
 
I

Ian Collins

I am using Mingw 3.4.5 on a 'Doze box, but it pretty much
follows any Gnu compiler I've run into.

Why is this:

Because const is broken in C.
The int size is invariant, and ... "morally"
equivalent to using SIZE, yet it won't let me do that.

And yes, I understand/speak/grok preprocessor vs.
compiler. I know *how*, I just wonder *why*. Is
this a practical, strategic or tactical thing,
or am I just being silly? Was it a conscious decision
of the committee, or just one of those things?

My guess would be an oversight in the original standard. It's
unfortunate that two opportunities to correct it have now been missed.
 
E

Eric Sosman

I am using Mingw 3.4.5 on a 'Doze box, but it pretty much
follows any Gnu compiler I've run into.

Why is this:

---------------------------
#define SIZE 20
const int size = SIZE;
char array[SIZE];
---------------------------

legal, yet

---------------------------
#define SIZE 20
const int size = SIZE;
char array[size];
---------------------------

not?

The int size is invariant, and ... "morally"
equivalent to using SIZE, yet it won't let me do that.

And yes, I understand/speak/grok preprocessor vs.
compiler. I know *how*, I just wonder *why*. Is
this a practical, strategic or tactical thing,
or am I just being silly? Was it a conscious decision
of the committee, or just one of those things?

`const' doesn't mean "constant." Roughly speaking, it means
"unwritable" -- and even that's not quite true, because a
`const volatile' object might be writable by something outside C
itself (a hardware timer, for example, might be read-only to the
C program but writeable by a little silicon doodad).

With that in mind, back to your question. In `char array[SIZE];'
the macro is replaced by the token `20', and that token represents
an integer literal, something that is in fact constant. But in
`char array[size];', `size' is a variable that will acquire some
value when the program is eventually executed. In this case it is
easy to see what the (initial) value will be, but nonetheless: It's
a variable, not a constant.

Just for added fun (or confusion), the "C99" version of the C
Standard added a rule to the language that allows for a "variable-
length array" whose dimension is computed at run-time rather than
at compile time. Taken as a VLA, `char array[size];' is perfectly
legal: It's an array with `size' elements (whatever `size' happens
to be at the moment), and which gets memory allocated to it when the
declaration is reached. However, file-scope arrays (and `static'
arrays inside functions) get their memory before the program even
starts, so their dimensions must be known prior to execution. A
VLA must have `auto' storage, meaning that it can only exist inside
a function.
 
L

Les Cargill

Eric said:
I am using Mingw 3.4.5 on a 'Doze box, but it pretty much
follows any Gnu compiler I've run into.

Why is this:

---------------------------
#define SIZE 20
const int size = SIZE;
char array[SIZE];
---------------------------

legal, yet

---------------------------
#define SIZE 20
const int size = SIZE;
char array[size];
---------------------------

not?

The int size is invariant, and ... "morally"
equivalent to using SIZE, yet it won't let me do that.

And yes, I understand/speak/grok preprocessor vs.
compiler. I know *how*, I just wonder *why*. Is
this a practical, strategic or tactical thing,
or am I just being silly? Was it a conscious decision
of the committee, or just one of those things?

`const' doesn't mean "constant."

Agreed - and that's probably the most important thing I'm
"missing" here. Ah, the const keyword...

Even after all these years, I *really* want it to mean
that...
Roughly speaking, it means
"unwritable" -- and even that's not quite true, because a
`const volatile' object might be writable by something outside C
itself (a hardware timer, for example, might be read-only to the
C program but writeable by a little silicon doodad).

Sure. "Not writable by this program". And when I must mix const and
volatile, I still end up reading about it...

What we usually want is a const pointer to a volatile object. But
not always - for things like... DMA controllers ( in kernel mode )
we may express pointers in hardware registers... so it may be
a const pointer to a volatile *pointer*.

I did a quick Google, and it seems to be a serious point of confusion.
A thing to translate all the permutations of const/static/volatile
to English would be very handy...

The examples seem to avoid pointer syntax, which is unfortunate.
With that in mind, back to your question. In `char array[SIZE];'
the macro is replaced by the token `20', and that token represents
an integer literal, something that is in fact constant.
Yessir.

But in
`char array[size];', `size' is a variable that will acquire some
value when the program is eventually executed. In this case it is
easy to see what the (initial) value will be, but nonetheless: It's
a variable, not a constant.

And a declaration cannot depend on a variable, whether it is
invariant or not...
Just for added fun (or confusion), the "C99" version of the C
Standard added a rule to the language that allows for a "variable-
length array" whose dimension is computed at run-time rather than
at compile time. Taken as a VLA, `char array[size];' is perfectly
legal: It's an array with `size' elements (whatever `size' happens
to be at the moment), and which gets memory allocated to it when the
declaration is reached. However, file-scope arrays (and `static'
arrays inside functions) get their memory before the program even
starts, so their dimensions must be known prior to execution. A
VLA must have `auto' storage, meaning that it can only exist inside
a function.
 
L

Les Cargill

<snip>

Here's a terrible example:

http://icecube.wisc.edu/~dglo/c_class/const_vol.html

int
check_iobuf(void)
{
volatile int iobuf;
int val;

while (iobuf == 0) {
}
val = iobuf;
iobuf = 0;
return(val);
}

Unless I miss my guess, "iobuf" is an *auto*. It's
not mapped as a hardware register, no external
asynchronous process can change it.... it's on
the local stack...

I suppose you can have it be "extern volatile int iobuf" and
use the linker to put it in a hardware-interesting "segment"
but imagine the confusion promulgated by that bad example. it's
even apparently an infinite loop...

One compiler for 68000 used an "@" operator to
declare something as "int Thing = @0x50000;" for
this... I don't have an example and am working from memory.
 
K

Keith Thompson

Les Cargill said:
I am using Mingw 3.4.5 on a 'Doze box, but it pretty much
follows any Gnu compiler I've run into.

Why is this:

Both are legal if they occur inside a function definition, where the
second is a variable-length array (VLA) declaration.

At file scope, VLAs are not allowed.

Given:

const int size = 20;

"size" is still considered an ordinary object; a reference to it is
*not* a constant expression. Any compiler worth its salt will convert a
reference to that object to a literal 20 (assuming that results in
faster code), but that's an optimization; in the abstract machine it
fetches the value of the object (which will always be 20).

The key thing to remember is that the "const" keyword doesn't mean
"constant". A "constant" expression is one that can be (and in many
cases must be) evaluated at compile time. The "const" keyword really
means read-only. For example, the following is a perfectly legal
declaration (inside a function):

const int r = rand();

(Note that C++ has slightly different rules; in C++, a reference to
"size" *is* a constant expression.)
 
L

Les Cargill

Keith said:
Les Cargill said:
I am using Mingw 3.4.5 on a 'Doze box, but it pretty much
follows any Gnu compiler I've run into.

Why is this:

Both are legal if they occur inside a function definition, where the
second is a variable-length array (VLA) declaration.

I have not been able to find the -c99 switch for MingW 3.4.5.... don't
think it does it...
At file scope, VLAs are not allowed.

Given:

const int size = 20;

"size" is still considered an ordinary object; a reference to it is
*not* a constant expression. Any compiler worth its salt will convert a
reference to that object to a literal 20 (assuming that results in
faster code), but that's an optimization; in the abstract machine it
fetches the value of the object (which will always be 20).

Well, there you go. "const" isn't really const... it's a 'C' term of
art....
The key thing to remember is that the "const" keyword doesn't mean
"constant". A "constant" expression is one that can be (and in many
cases must be) evaluated at compile time. The "const" keyword really
means read-only. For example, the following is a perfectly legal
declaration (inside a function):

const int r = rand();

(Note that C++ has slightly different rules; in C++, a reference to
"size" *is* a constant expression.)

Right! We are in an infinity of twisty mazes :)
 
N

Nobody

And yes, I understand/speak/grok preprocessor vs.
compiler. I know *how*, I just wonder *why*. Is
this a practical, strategic or tactical thing,
or am I just being silly? Was it a conscious decision
of the committee, or just one of those things?

K&R C didn't have "const". Along with "volatile", it was added during the
standardisation process to facilitate optimisation.

The main purpose of "const" is to tell the compiler to assume that the
value won't change. However, a const-qualified variable is still an
lvalue, not a compile-time constant; changing that was presumably
considered too radical.

If you want a compile-time integer constant, you can use enum, e.g.:

enum { size = SIZE };

This "trick" is still quite common in C++ code due to some compilers being
a bit late to accept that a "static const" member really is a compile-time
constant in C++, and the preprocessor not being an acceptable substitute
when the name has to belong to a class or namespace.
 
I

Ian Collins

K&R C didn't have "const". Along with "volatile", it was added during the
standardisation process to facilitate optimisation.

The main purpose of "const" is to tell the compiler to assume that the
value won't change. However, a const-qualified variable is still an
lvalue, not a compile-time constant; changing that was presumably
considered too radical.

If you want a compile-time integer constant, you can use enum, e.g.:

enum { size = SIZE };

This "trick" is still quite common in C++ code due to some compilers being
a bit late to accept that a "static const" member really is a compile-time
constant in C++, and the preprocessor not being an acceptable substitute
when the name has to belong to a class or namespace.

Only in code written in the last century!
 
J

James Kuyper

On 06/02/2012 06:50 PM, Nobody wrote:
....
The main purpose of "const" is to tell the compiler to assume that the
value won't change.

I don't think that's the main purpose. In my opinion, the most important
thing about 'const', and the reason why I use it where appropriate, is
that it turns many dangerous constructs into constraint violations,
making a diagnostic mandatory.
 
K

Keith Thompson

Les Cargill said:
Keith Thompson wrote: [...]
I have not been able to find the -c99 switch for MingW 3.4.5.... don't
think it does it...

Try "-std=c99".
Well, there you go. "const" isn't really const... it's a 'C' term of
art....

"const" is a C keyword. Unlike "constant", it's not an English word
with a pre-existing meaning. It means exactly what the language says it
means.

"const" does mean "const". It just doesn't mean "constant".
Right! We are in an infinity of twisty mazes :)

And even in C++, given:

const int r = rand();

a reference to r is not a constant expression. (It can't be; there's no
way to evaluate it at compile time.)

BTW, there is a limited workaround. If you want size to be a constant
expression, without using the preprocessor, you can write:

enum { size = 20 };

Enumeration constants are of type int, and references to them are
constant expressions. You can't use this to create constants of types
other than int, though.
 
L

Les Cargill

Keith said:
Les Cargill said:
Keith Thompson wrote: [...]
I have not been able to find the -c99 switch for MingW 3.4.5.... don't
think it does it...

Try "-std=c99".
Well, there you go. "const" isn't really const... it's a 'C' term of
art....

"const" is a C keyword. Unlike "constant", it's not an English word
with a pre-existing meaning. It means exactly what the language says it
means.

"const" does mean "const". It just doesn't mean "constant".
Right! We are in an infinity of twisty mazes :)

And even in C++, given:

const int r = rand();

a reference to r is not a constant expression. (It can't be; there's no
way to evaluate it at compile time.)

BTW, there is a limited workaround. If you want size to be a constant
expression, without using the preprocessor, you can write:

enum { size = 20 };

Enumeration constants are of type int, and references to them are
constant expressions. You can't use this to create constants of types
other than int, though.


I like the enum trick - hadn't seen that before.
 
B

BartC

Les Cargill said:
Keith Thompson wrote:

Right! We are in an infinity of twisty mazes :)

I've never got the hang of 'const' in C.

Using it like this:

const int a = 1000;

means a gets the value 1000, then can't be changed. OK.

Now you try this:

int b=2000;
const int* p = &b;

means, I think, that you can't change b via the the pointer. But you *can*
change p to point somewhere else. To protect p itself, it's necessary to
write:

int* const p = &b;

Now you can write: *p=3000; but you can't write p=&c. (I've just looked this
up online which is how I know...)

To keep p readonly, and not allow it's target to be changed either,
presumably you have to write:

const int * const p = &b;

Perfectly clear :)

But, who care's about that; what I want to know is why, at some point during
the last forty years, didn't someone just add a feature like:

constant int d = 4000;

and it means exactly that: d is a synonym for the literal d. It has a
precise type, and follows normal scoping rules. You also can't use it as an
lvalue so it is guaranteed to have that value. It can also be used to
initialise fixed size arrays, and does not to be written in capitals.

It's a five-minute job to add to the language (exaggerating a little, but it
is pretty trivial to implement). Instead we have a series of unsatisfactory
alternatives: #define, const (which doesn't work, or works but you
unintentionally end up with a VLA), and enum{}, which you described as a
'trick'...
 
N

Nobody

Only in code written in the last century!

Or code intended to work with compilers written in the last century.

For large code bases, it's not uncommon to just keep using the compiler
for which the very first lines of code were written, which then requires
keeping all subsequent code compatible with that compiler.

Although I don't think you'd actually have to go back 12 years to find a
compiler that didn't handle "static const" correctly.
 
M

Malcolm McLean

בת×ריך ×™×•× ×¨×שון, 3 ביוני 2012 02:11:15 UTC+1, מ×ת James Kuyper:
On 06/02/2012 06:50 PM, Nobody wrote:
...

I don't think that's the main purpose. In my opinion, the most important
thing about 'const', and the reason why I use it where appropriate, is
that it turns many dangerous constructs into constraint violations,
making a diagnostic mandatory.
I never use it. Part of the reason is that whilst int member(const FOO *obj) is clear enough in intention, in fact the constness of obj does not extend to its members. Another is the const-poisoning problem. You have to use const consistently, or you end up needing to pass a const pointer to a non-const subroutine which doesn't in fact alter the data.
Then C++ added mutable. This makes a member of a const class non-const. Thefact that mutable was considered necessary is an argument against const.

But the main reason is simply visual clutter. You want to be able to see what a function does at a glance.
 
J

James Kuyper

On 06/03/2012 10:39 AM, Malcolm McLean wrote:
....
does not extend to its members. Another is the const-poisoning
problem. You have to use const consistently, or you end up needing to
pass a const pointer to a non-const subroutine which doesn't in fact
alter the data.

I don't see that as a problem. Forcing consistent use of const is good
mental discipline; the casts that are needed to interface with libraries
that don't use it correctly helps document those library's faults.
 
B

Ben Bacarisse

Malcolm McLean said:
בת×ריך ×™×•× ×¨×שון, 3 ביוני 2012 02:11:15 UTC+1, מ×ת James Kuyper:
I never use it. Part of the reason is that whilst int member(const FOO
*obj) is clear enough in intention, in fact the constness of obj does
not extend to its members.

Can you say what you mean by this? It contradicts what I thought was
the case (assuming you meant the members of *obj).

<snip>
 
K

Keith Thompson

BartC said:
But, who care's about that; what I want to know is why, at some point
during the last forty years, didn't someone just add a feature like:

constant int d = 4000;

and it means exactly that: d is a synonym for the literal d. It has a
precise type, and follows normal scoping rules. You also can't use it
as an lvalue so it is guaranteed to have that value. It can also be
used to initialise fixed size arrays, and does not to be written in
capitals.

It's a five-minute job to add to the language (exaggerating a little,
but it is pretty trivial to implement). Instead we have a series of
unsatisfactory alternatives: #define, const (which doesn't work, or
works but you unintentionally end up with a VLA), and enum{}, which
you described as a 'trick'...

In fact someone did do that: Bjarne Stroustrup, when he was designing
C++.

Except that he didn't introduce a new keyword; he just tweaked the
semantics of "const".

In C++, if you declare

const some_type x = init_expr;

x becomes a constant expression *if* some_type is arithmetic *and*
init_expr is a constant expression. Otherwise, it has pretty much the
same meaning it has in C. (I probably haven't gotten that 100% right,
but that's the general idea.)

So, for example, in C++ (which doesn't have VLAs) you can have this:

{
const int array_size = 20;
int arr[arr_size];
}

but not this:

{
const int array_size = rand() % 20 + 1;
int arr[arr_size]; // invalid
}

I don't think it would have been terribly difficult for C to adopt this
feature, and I think it would have been a good idea. Maybe for C202x.
 
I

Ian Collins

בת×ריך ×™×•× ×¨×שון, 3 ביוני 2012 02:11:15 UTC+1, מ×ת James Kuyper:

Please please wrap your lines!
I never use it. Part of the reason is that whilst int member(const FOO *obj)
is clear enough in intention, in fact the constness of obj does not extend
to its members.

Eh? What if FOO is in read only memory?
Another is the const-poisoning problem. You have to use const consistently,
or you end up needing to pass a const pointer to a non-const subroutine
which doesn't in fact alter the data.

So good coding discipline is bad?
Then C++ added mutable. This makes a member of a const class non-const.
The fact that mutable was considered necessary is an argument against
const.

Mutable is rarely used in C++. Most of the cases in my own code are for
internal state variables.
But the main reason is simply visual clutter. You want to be able to see
what a function does at a glance.

Isn't whether the function modifies its arguments one of the most
important things you want to see at a glance?
 
K

Keith Thompson

Ben Bacarisse said:
Can you say what you mean by this? It contradicts what I thought was
the case (assuming you meant the members of *obj).

<snip>

Agreed.

typedef struct foo {
int x;
} FOO;

int member(const FOO *obj) {
obj->x = 42;
return 43;
}

int main(void) {
FOO foo_obj;
int m = member(&foo_obj);
return 0;
}

c.c: In function ‘member’:
c.c:6:5: error: assignment of member ‘x’ in read-only object
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top