Is enum a suitable way to implement a "local define?"

J

James Kuyper

True, but it also means that there will be more battles about backwards
compatibility, and they can't be solved by making an "auto.h" header
containing "#define auto __auto_type". The C standards people are
always a bit hesitant (with good reason) to make a change that means
previously correct code is suddenly illegal.

I agree with that approach in general, but I think that "auto" is a
reasonable exception. This is because it was almost never necessary, and
therefore almost never used. I only say "almost" because I remember one
person describing using 'auto' in a declaration that was part of the
expansion of a macro, to prevent the macro from being used at file scope
- but that's a pretty obscure case. I'm not sure whether C++ ever
supported the meaning that 'auto' has in C, but I think that it did. If
so, they successfully moved to the new meaning without using _Auto,
which suggests that C can do the same.
 
S

Stefan Ram

James Kuyper said:
so, they successfully moved to the new meaning without using _Auto,
¯¯¯¯¯¯¯¯¯¯¯¯
TIOBE Programming Community index, as of March 2014

C 18 %
C++ 6 % -3 % compared to one year ago
 
I

Ike Naar

Why can't I declare abc as a synonym for 5678, but be stopped from writing
&abc? After all I can used #define or enum to declare abc, with some
limitations, and I can't write &abc then either.

register int const abc = 5678;
 
K

Keith Thompson

James Kuyper said:
The proposal is to adopt C++ rules: a integer const variable initialized
with a integer constant expression is itself an integer constant
expression; as such, it would be usable in those contexts.

The C++ rule, as I understand it, is that if a const-qualified object is
initialized with an integer constant expression, then the name of the
object (when not used in a context requiring an lvalue) is a constant
expression. The object itself is still an object.

const int a = 42;
double this_is_not_a_VLA[a];
const int *but_a_has_an_address = &a;

[...]
 
K

Keith Thompson

David Brown said:
On 09/04/14 00:27, BartC wrote: [...]
Instead of a simple, logical extension to allow such a declaration,
where it will be completely obvious what it is and what it can do,
you instead propose using a *variable* for the purpose, which so many
issues associated with it that I hardly know where to start! But I
will have a go:

A "const" is not a variable - it is a constant object. So instead of
introducing some sort of new type of constant to the language, I would
rather take the existing const concept and extend it slightly (as it is
in C++) to cover the remaining needs for constants. That is far
simpler, clearer and easier than having a new type of constant - and it
already exists in C++.

You're conflating "const" and "constant" in a context where the
distinction is extremely relevant.

It's not entirely clear what the word "variable" should mean in C. The
standard doesn't use the term; instead it uses "object". Is a
const-qualified object a "variable"? You could probably get three
different answers from three different people here; mine is that I avoid
using the word "variable" when there's any chance of confusion.

"const" in C simply means "read-only". "Constant" refers to something
that can (and must) be evaluated at compile time, as in "integer
constant" (what some languages call an "integer literal") or a "constant
expression" (an expression that can be used as if it were a literal).

Remember that

const int r = rand();

is perfectly valid (at block scope); r is const (read-only), but clearly
its value cannot be determined until run time.

[...]
"const" is not a hint to the compiler about variables - it is a specific
direction that this object will be constant, and will never be changed.

It's a direction that the object is *read-only*.

[...]
I think you are mixing up the use of "const" in definitions such as
"static const int xyz = 123;", and its use in function arguments
(especially for pointers), such as "size_t strlen(const char* s)".

They're essentially the same thing. In both cases, the "const" asserts
that the relevant object (xyz or *s) will not be modified. In both
case, the compiler can optimize based on that assertion. It just
happens to be able to optimize better when it can see what the initial
(and only) value is.

What the C++ rule does is take an object that's defined to be read-only
and make its name a compile-time constant. IMHO it's a bit of a hack,
but it's a useful one that I'd like to see adopted in C.

[...]
 
D

David Brown

The discussion looks like it is about an extension to C. C++'s
constexpr would be a useful extension to C that meets your requirements.


A constexpr isn't a variable.

In Pascal, however, some CONST's /are/ variables - in Borland Pascal
(and Delphi), a so-called "typed constant", such as "const i : integer =
123;", is equivalent to "static int i = 123;" in C - it is just a
persistent initialised variable. C is not alone among programming
languages in having the occasional bit of confusing syntax!

A constexpr is not a variable - and neither is a plain "const" or
"static const" in C.

"constexpr" for objects is useful in C++ because there "const" means "as
far as /you/ can tell, this thing is constant - you can't legally change
it, and you can rely on it looking unchanged". But an object in C++ can
be "const" and still have bits ("mutable" bits) that get changed behind
the scenes. So "constexpr" is an extra level above "const" to guarantee
that the object is absolutely fixed at compile-time. It's a level of
complication that is not really needed in C.

"constexpr" for functions, however, could be useful in C (as in C++) as
it marks a function for evaluation at compile time, and lets you use the
function and its results as compile-time constants.
 
D

David Brown

On 04/08/2014 06:27 PM, BartC wrote:

It depends upon the scope. It's stupid that C gives two entirely
different meanings to 'static' (there's also a third one that's not
relevant to this question); but it's a historical artifact, and changing
the standard now to cleanly separate the two concepts would break too
much code.

At block scope, static determines whether the variable has automatic or
static storage duration. Now, you can't write code with defined behavior
that changes the value of the variable, and if it has automatic storage
duration, there's no way to write code with defined behavior that checks
what the value is between calls to the function. Therefore, a conforming
implementation could treat a "const int" precisely the same as a "static
const int", reducing unnecessary memory usage and unnecessary
re-initialization.

That's not quite true; a block scope "static const int" would have the
same exact address during every pass through function. That could also
be true if it had automatic storage duration, so is not itself a problem
- unless the function is directly or indirectly recursive, and compares
the addresses. It's not clear to me that optimizing "const int" by
treating it the same as "static const int" would be permitted for
recursive functions - which is an argument for explicitly using "static
const int".

At file scope, "static" determines whether the variable has internal or
external linkage. If it has internal linkage, and the program never
takes it's address, no actual storage need be allocated for it, the same
as at block scope. However, if it has external linkage, then there can
only be one definition for the variable across all of the translation
units that make up the program. It's value would have to be unknown in
every translation unit other than the one where it is defined. As a
result, it wouldn't be a compile time constant in those other modules,
which would pretty much destroy the whole point of defining such a
variable. Again, the preferred declaration is "static const int".

I like to think of "static" as meaning "static allocation", i.e., a
fixed address and lifetime throughout the program, and minimal scope and
linkage. Although that combines two different concepts in one keyword,
it applies to both file scope and block scope "static" - and I think
that reduces the confusion a little.

One difference between the uses is that at file scope you should use
"static" whenever possible, as it makes the program more modular,
reduces namespace collisions, and lets the compiler generate better code
(sometimes removing the "static" data or function altogether). In block
scope, on the other hand, you should normally only use "static" if you
actually need it, as it generally makes code less efficient. An
exception is block scope const's with compile-time known initialisers -
making these "static const" is generally a good idea.


I would have preferred "static" to mean only the static allocation part,
that internal linkage was the default in C, and that an extra keyword
(such as "export" or "public") would be needed to give an object or
function external linkage. Defaulting to global external linkage was a
fundamental error in the C language design, IMHO. But it is too late to
change now.
 
D

David Brown

David Brown said:
On 09/04/14 00:27, BartC wrote: [...]
Instead of a simple, logical extension to allow such a declaration,
where it will be completely obvious what it is and what it can do,
you instead propose using a *variable* for the purpose, which so many
issues associated with it that I hardly know where to start! But I
will have a go:

A "const" is not a variable - it is a constant object. So instead of
introducing some sort of new type of constant to the language, I would
rather take the existing const concept and extend it slightly (as it is
in C++) to cover the remaining needs for constants. That is far
simpler, clearer and easier than having a new type of constant - and it
already exists in C++.

You're conflating "const" and "constant" in a context where the
distinction is extremely relevant.

Perhaps - I certainly agree that we must be as clear as possible here.

I think one other aspect that we should make clear is when we are
talking about file level objects, and when we are talking about function
local objects. There is a significant difference in what can and cannot
be done with "const" in these contexts, and I think that is adding to
Bart's confusion.
It's not entirely clear what the word "variable" should mean in C. The
standard doesn't use the term; instead it uses "object". Is a
const-qualified object a "variable"? You could probably get three
different answers from three different people here; mine is that I avoid
using the word "variable" when there's any chance of confusion.

I view "variable" to mean something that can legally be changed after
its creation and initialisation. Thus any object defined as "const"
will be a constant object, and not a variable.
"const" in C simply means "read-only". "Constant" refers to something
that can (and must) be evaluated at compile time, as in "integer
constant" (what some languages call an "integer literal") or a "constant
expression" (an expression that can be used as if it were a literal).

Yes, that's true (AFAIUI). But I think we must carefully distinguish
between file scope const and block scope const, as the rules and effects
are different.

Because a file scope initialised "const" must be initialised by a
"constant expression", and it is read only, the compiler knows its value
at any time - and therefore any use of its value can be replaced by the
compile-time constant expression. The exceptions here are for things
like array dimensions, which C disallows even though the compiler is
guaranteed the required knowledge to generate the code, and C++ can use
the const objects in such circumstances.

At block level (local to a function), const objects become constant
after their initialisation - you cannot legally change them. But
(unless they are "static const") they do not necessarily have
compile-time constant values, and are therefore named read-only copies
of their initialisers.

Remember that

const int r = rand();

is perfectly valid (at block scope); r is const (read-only), but clearly
its value cannot be determined until run time.

Correct - but after its initialisation, and throughout its lifetime, "r"
is constant.

I believe Bart has been thinking mainly of file scope "const", but has
mixed in block scope differences too. I wish I had thought of that
earlier - I think we could have had a clearer and simpler discussion if
the distinction had been made clear.
[...]
"const" is not a hint to the compiler about variables - it is a specific
direction that this object will be constant, and will never be changed.

It's a direction that the object is *read-only*.

[...]
I think you are mixing up the use of "const" in definitions such as
"static const int xyz = 123;", and its use in function arguments
(especially for pointers), such as "size_t strlen(const char* s)".

They're essentially the same thing. In both cases, the "const" asserts
that the relevant object (xyz or *s) will not be modified. In both
case, the compiler can optimize based on that assertion. It just
happens to be able to optimize better when it can see what the initial
(and only) value is.

While that is true, the two uses of "const" here have different effects
because of the different levels of knowledge that the compiler has. I
am trying to concentrate on the effects, implementations and practical
features (and limitations) of "const", rather than the language details.
What the C++ rule does is take an object that's defined to be read-only
and make its name a compile-time constant. IMHO it's a bit of a hack,
but it's a useful one that I'd like to see adopted in C.

Well, at least we agree on what we want in the end!
 
B

BartC

Stefan Ram said:
Stephen Sprunk said:
"Declarations in C must be read in an `inside-out' style that many find
difficult to grasp [Anderson 80]. Sethi [Sethi 81] observed that many of

This is an expression:

*(**(*(*a[1])())[2][3]);

. This is a declaration:

int *(**(*(*a[1])())[2][3]);

Cdecl tells me:

a is array 1 of pointer to function returning pointer to array 2 of array 3
of pointer to pointer to pointer to int.

The fact that I had to use an external tool to understand a language syntax
is a major fail.
This is an expression:

*(**(*(*a[1])())[2][3]);

The expression only relates to the declaration *if* you are indexing the
array, calling the function it points to, *and* fully indexing and
dereferencing the result . This need not be the case.

(In my own syntax this type, although meaningless, might be written
left-to-right as:

[1]ref function=>ref [2,3]ref ref ref int a

But as currently translated to C, it puts the () in the wrong place. And I'd
thought I had C type generation sussed long ago. Another fail!)
 
J

James Kuyper

Stefan Ram said:
Stephen Sprunk said:
"Declarations in C must be read in an `inside-out' style that many find
difficult to grasp [Anderson 80]. Sethi [Sethi 81] observed that many of

This is an expression:

*(**(*(*a[1])())[2][3]);

. This is a declaration:

int *(**(*(*a[1])())[2][3]);

Cdecl tells me:

a is array 1 of pointer to function returning pointer to array 2 of array 3
of pointer to pointer to pointer to int.

The fact that I had to use an external tool to understand a language syntax
is a major fail.
This is an expression:

*(**(*(*a[1])())[2][3]);

The expression only relates to the declaration *if* you are indexing the
array, calling the function it points to, *and* fully indexing and
dereferencing the result . This need not be the case.

If you can understand how to use an identifier in that particular way,
you should be able to understand the corresponding parts of the
declaration; the fact that there are additional ways you can use the
identifier shouldn't make the declaration any harder to understand (the
various specifiers and qualifiers are a different matter, of course -
they don't mirror usage).

Because C declarations reflect one possible usage of the identifier
being declared, if the declaration of an identifier is too hard for you
to understand, that merely reflects the fact that the identifier's
meaning is probably too complicated for you to safely use it.
 
J

James Kuyper

On 09/04/14 01:39, James Kuyper wrote: .... ....
I like to think of "static" as meaning "static allocation", i.e., a
fixed address and lifetime throughout the program, and minimal scope and
linkage. Although that combines two different concepts in one keyword,
it applies to both file scope and block scope "static" - and I think
that reduces the confusion a little.

But you can get a fixed address and lifetime throughout the program
without using the 'static' keyword, just by declaring the object at file
scope. Similarly, you can get minimal scope and linkage without using
the 'static' keyword, just by declaring the object with block scope.

It was a mistake to use the same keyword for making both kinds of
distinctions - it causes frequent confusion - and I think that trying to
create a merged concept that justifies that mistake is a further mistake.
 
N

Noob

David said:
You are arguing for language extensions, and you don't even understand
the basics of C as it is?

Ding! Ding! Ding! We have a winner!

You have to hand it to him, "BartC" is very effective at what he does
(trolling, that is). Just look at how many replies he elicits in this
very thread! And after all this time, he still doesn't get C, FFS!

It's hard to ignore him when so many people reply to him.

Think of the children!
 
D

David Brown

But you can get a fixed address and lifetime throughout the program
without using the 'static' keyword, just by declaring the object at file
scope. Similarly, you can get minimal scope and linkage without using
the 'static' keyword, just by declaring the object with block scope.

You can't get file scope with internal linkage without using "static".
It was a mistake to use the same keyword for making both kinds of
distinctions - it causes frequent confusion - and I think that trying to
create a merged concept that justifies that mistake is a further mistake.

I agree that the two concepts should not have been mixed (as noted in
the part of my post that you snipped here). So I am not trying to
justify it - I am just trying to give a meaning to the "static" keyword
which covers its practical effects. I have found it helpful in the past
when answering the common question "what does "static" mean in C?", so I
repeated it here in case other people found it helpful.
 
D

David Brown

Ding! Ding! Ding! We have a winner!

You have to hand it to him, "BartC" is very effective at what he does
(trolling, that is). Just look at how many replies he elicits in this
very thread! And after all this time, he still doesn't get C, FFS!

It's hard to ignore him when so many people reply to him.

Think of the children!

I don't think BartC is a troll, and I think he raises some interesting
points. Sometimes he misunderstands parts of C, and is quick to suggest
improvements or extensions when the issue is actually already covered in
C, or when they would conflict with key features of C. When that
happens, my aim is to help him - just as others here have helped me when
I have had questions or got things wrong.
 
K

Keith Thompson

Noob said:
Ding! Ding! Ding! We have a winner!

You have to hand it to him, "BartC" is very effective at what he does
(trolling, that is). Just look at how many replies he elicits in this
very thread! And after all this time, he still doesn't get C, FFS!

It's hard to ignore him when so many people reply to him.

Think of the children!

We have no shortage of genuine trolls here. I don't think BartC is one
of them.
 
K

Keith Thompson

David Brown said:
David Brown said:
On 09/04/14 00:27, BartC wrote: [...]
Instead of a simple, logical extension to allow such a declaration,
where it will be completely obvious what it is and what it can do,
you instead propose using a *variable* for the purpose, which so many
issues associated with it that I hardly know where to start! But I
will have a go:

A "const" is not a variable - it is a constant object. So instead of
introducing some sort of new type of constant to the language, I would
rather take the existing const concept and extend it slightly (as it is
in C++) to cover the remaining needs for constants. That is far
simpler, clearer and easier than having a new type of constant - and it
already exists in C++.

You're conflating "const" and "constant" in a context where the
distinction is extremely relevant.

Perhaps - I certainly agree that we must be as clear as possible here.
[...]
It's not entirely clear what the word "variable" should mean in C. The
standard doesn't use the term; instead it uses "object". Is a
const-qualified object a "variable"? You could probably get three
different answers from three different people here; mine is that I avoid
using the word "variable" when there's any chance of confusion.

I view "variable" to mean something that can legally be changed after
its creation and initialisation.

That's not an unreasonable definition -- but it's still not entirely
unambiguous. Someone might refer to a const-qualified object as a
"variable". And a member of a non-const struct object is an object that
can be modified, but is it a variable or just part of one? What about
an array element? What about an object allocated by malloc()?

The words "const", "constant", and "object", have well-defined meanings
in C. "Variable" doesn't. I suggest just avoiding that word, at least
in this discussion.
Thus any object defined as "const"
will be a constant object, and not a variable.

Um, no, it will be a *read-only* object.
Yes, that's true (AFAIUI). But I think we must carefully distinguish
between file scope const and block scope const, as the rules and effects
are different.

Because a file scope initialised "const" must be initialised by a
"constant expression", and it is read only, the compiler knows its value
at any time - and therefore any use of its value can be replaced by the
compile-time constant expression. The exceptions here are for things
like array dimensions, which C disallows even though the compiler is
guaranteed the required knowledge to generate the code, and C++ can use
the const objects in such circumstances.

There's not that much difference. The name of a const-qualified object
is not a constant expression, regardless of where it's defined.
Optimizing compilers can evaluate any expression at compile time if they
have enough information. A file-scope object might be defined in one
translation unit and declared in another; the compiler might see a
declaration "const int foo;" and have no way of knowing the value.

A compiler can replace:

int n = 42;
printf("n = %d\n", n);

by the equivalent of:

puts("n = 42");

even though there's no "const" on the declaration.
At block level (local to a function), const objects become constant
after their initialisation - you cannot legally change them. But
(unless they are "static const") they do not necessarily have
compile-time constant values, and are therefore named read-only copies
of their initialisers.

The same is true of file-scope const objects. Such an object has an
address, and the value of the initializer is stored at that address.
The ability to refer to that value without necessarily loading it from
memory is merely an optimization.
Correct - but after its initialisation, and throughout its lifetime, "r"
is constant.

No, it's read-only, not constant. In C, the word "constant"
refers to something that can be evaluated at compile time. We can
reasonably discuss this only if we use words in ways that are
consistent with with the way they're defined by the C standard.

[...]
 
K

Keith Thompson

David Brown said:
On 09/04/14 13:27, James Kuyper wrote: [...]
It was a mistake to use the same keyword for making both kinds of
distinctions - it causes frequent confusion - and I think that trying to
create a merged concept that justifies that mistake is a further mistake.

I agree that the two concepts should not have been mixed (as noted in
the part of my post that you snipped here). So I am not trying to
justify it - I am just trying to give a meaning to the "static" keyword
which covers its practical effects. I have found it helpful in the past
when answering the common question "what does "static" mean in C?", so I
repeated it here in case other people found it helpful.

An object defined within a function definition has block scope,
automatic storage duration, and no linkage by default. The "static"
keyword changes the storage duration from automatic to static,
without affecting the scope or linkage.

An object defined outside any function definition has file
scope, static storage duration, and external linkage by default.
The "static" keyword changes the linkage from external to internal,
without affecting the scope or storage duration.

I don't think there's any reasonable way to view those as a single
meaning.
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top