The compiler knows the value of the variable at compile time and is free
to optimise away (by substitution of the constant value) access to the
variable. So why can't it be used elsewhere as a compile time constant?
Was this subject discussed during the development of C99? I can't think
of a situation where existing code would break.
--
Ian Collins.
Because of the history of 'const' in ISO C. The 'const' specifier
was added to the existing language semantics, rendering such
variables as read-only (unmodifiable), without any other benefits
or changes to the semantics of the language.
The 'const' of ISO C++ took a different route, although it
still took a few revisions to the language spec before const
integer variables could be used as actual compile-time
constants.
Theoretically (I think), C could be enhanced to allow const
variables to be used as compile-time constants, although
there may be some complications with extern const variables.
-drt
--
Ian Collins.
The history of "const" in C is that some members of the committee
thought it really meant const and other members thought it just meant
read-only, but we didn't discover the disconnect until very late in the
process and then had to make a decision one way or the other in very
little time. Read-only won since it seemed to be the safer choice at
the time, but I suspect you're right that it could be changed with very
little, if any, breakage.
-Larry Jones
I take it there's no qualifying exam to be a Dad. -- Calvin
lawrenc...@siemens.com wrote:
> Ian Collins <ian-...@hotmail.com> wrote:
> >
> > C could probably inherit all of the applicable const rules (including
> > linkage) from C++ without breaking any existing code. What surprised me
> > most in C99 was it didn't!
>
> The history of "const" in C is that some members of the committee
> thought it really meant const and other members thought it just meant
> read-only, but we didn't discover the disconnect until very late in the
> process and then had to make a decision one way or the other in very
> little time. Read-only won since it seemed to be the safer choice at
> the time, but I suspect you're right that it could be changed with very
> little, if any, breakage.
The one case that we keep running into where it makes a difference
is where developers expect const to actually be volatile read only
Things like serial numbers in embedded systems in rom is an example
where this is used const optimization prevents this from working
correctly.
w..
--
Ian Collins.
Ian Collins wrote:
That is fact is how we resolved this in our compilers to prevent
const optimization. I need to go back and re-read C99 but as I
understand it the default const is volatile.
We as this thread suggests optimize consts in our compilers.
w..
How do I go about submitting a proposal to make this change (assuming
one hasn't already been made)?
--
Ian Collins.
There could be an exception for volatile qualified consts -- what does
C++ do?
-Larry Jones
The authorities are trying to silence any view contrary to their own!
-- Calvin
lawrenc...@siemens.com wrote:
> Walter Banks <wal...@bytecraft.com> wrote:
> >
> > The one case that we keep running into where it makes a difference
> > is where developers expect const to actually be volatile read only
> > Things like serial numbers in embedded systems in rom is an example
> > where this is used const optimization prevents this from working
> > correctly.
>
> There could be an exception for volatile qualified consts -- what does
> C++ do?
I don't know what C++ does. The way we handled this in our embedded
system compilers was volatile const s were not optimized. Default
consts are.
w..
>
> The authorities are trying to silence any view contrary to their own!
> -- Calvin
That one would be apt for c.l.c :)
--
Ian Collins.
> Walter Banks <wal...@bytecraft.com> wrote:
> >
> > The one case that we keep running into where it makes a difference
> > is where developers expect const to actually be volatile read only
> > Things like serial numbers in embedded systems in rom is an example
> > where this is used const optimization prevents this from working
> > correctly.
>
> There could be an exception for volatile qualified consts -- what does
> C++ do?
Actually, const was broken in many ways in C++ 99, and I haven't
checked to see if they've fixed it since.
The C++ assignment operators (including the op= versions) yield an
lvalue, not an rvalue like the (badly worded) C standard specifies.
This is true even on built-in scalar and floating point types.
Assume a memory-mapped UART of the common 16C550 ilk, where the
transmit and receive registers share the same address, magically
mapped to the hardware by linker conjuring.
Consider poorly written code like this, whose purpose is to append a
'\n' after every '\r':
extern volatile unsigned char UART_tx;
void xmit_char(int ch)
{
if ('\r' == (UART_tx = ch))
UART_tx = '\n';
}
If it weren't for the test of the value written, maybe C++ could
optimize away the lvalue to rvalue conversion. But since the
assignment operator yields a volatile lvalue, the C++ volatile rules
(the same as C's) require that lvalue to rvalue conversion take place,
that is UART_tx must be freshly read after writing.
And that will read and discard a waiting character in the UART's input
buffer, if there is one. Not to mention probably omitting most '\n'
that should be appended and outputting the occasional incorrect one.
Their reason for making an assignment expression yielding the lvalue
assigned to makes sense in many of their uses of overloaded functions,
and also in binding references to lvalues. But it really breaks
volatile in situations like this if a compiler follows the letter of
the law.
If the same code is build in C, we have the ambiguously worded
6.5.16/3:
"An assignment operator stores a value in the object designated by the
left operand. An assignment expression has the value of the left
operand after the assignment, but is not an lvalue."
I realize that the second sentence means, and perhaps would be better
worded as:
"An assignment expression has the value of the right operand, after
conversion if necessary, to the type of the left operand."
Or some other wording that does not use the phrase "after the
assignment". The clause "bit is not an lvalue" pretty much states
that the destination lvalue does not need to be read back, even if
volatile, but it "after the assignment" could be misunderstood to
impose a requirement to read back a volatile.
I actually compiled code like this with a few compilers some years
back, at least MSVC6 and some version of CodeWarrior, and I think a
version of gcc.
In all cases, I examined the object code produced, and all three did
read back from the volatile object when the code was compiled as C++,
and did not when compiled as C.
--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
gcc:
movl 8(%ebp), %eax
movb %al, UART_tx
movb UART_tx, %al
cmpb $13, %al
SS:
movzbl 4(%esp),%eax
movb %al,UART_tx
cmpl $13,%eax
--
Ian Collins.
Isn't that a good thing? What use is an uninitialised (local) const?
> Also consider that certain types are not initializable, such as void or function.
I was only considering integral types.
> Optimization is usually an
> optional phase that you can choose to turn on or off, so that's not
> always reliable for standardization's sake.
I didn't say it was, I was simply using the current legality of
optimisation of const values in expressions to show that the compiler
knows the value at compile time. Which gives rise to the confusing
situation where in one context const integral types are compile time
constants while in others they are not.
--
Ian Collins.
On Apr 18, 5:49 pm, Ian Collins <ian-n...@hotmail.com> wrote:
> Isn't that a good thing?
It probably is a good thing, but till now it is not yet a requirement
that a compiler emit a diagnostic for absence of initializers.
> What use is an uninitialised (local) const?
I cannot say it is good programming practice, but it is possible to
declare a pointer to it and then use some casts to change its value.
> I was only considering integral types.
Consider these potential problems:
If an uninitialized const of other types gets casted to 'int', is that
then a compile time constant? If yes, should your compiler emit an
error message for that?
Or, if an initialized const of other types gets casted to 'int', is
that then a compile time constant? If yes, how do you make sure that
such casts yield the same results on different platforms?
Y.
>> I was only considering integral types.
>
> Consider these potential problems:
> If an uninitialized const of other types gets casted to 'int', is that
> then a compile time constant? If yes, should your compiler emit an
> error message for that?
OK, lets back up a step, if all const variables had to be initialised,
would this cause a problem?
> Or, if an initialized const of other types gets casted to 'int', is
> that then a compile time constant? If yes, how do you make sure that
> such casts yield the same results on different platforms?
>
You can't. A const variable of a non-integral type can't be used as a
compile time constant.
--
Ian Collins.
There is just enough weasel room in the "implementation defined"
clause for implementors to unnecessarily do the wrong thing and
claim that they conform, if they document this behavior.
A sensible progammer will not use volatile variables in complex
contexts.
UART_tx = ch;
if (ch == '\r')
UART_tx = '\n';
There is probably a bug anyway since there is no test for the
UART buffer being ready to accept another character.
> I realize that the second sentence means, and perhaps would be better
> worded as:
> "An assignment expression has the value of the right operand, after
> conversion if necessary, to the type of the left operand."
Yes, that is essentially the intent. It was certainly not meant
to require a readback in the abstract machine model.
<snip>
>> Or, if an initialized const of other types gets casted to 'int', is
>> that then a compile time constant? If yes, how do you make sure that
>> such casts yield the same results on different platforms?
>>
> You can't. A const variable of a non-integral type can't be used as a
> compile time constant.
>
Agreed. Float point const vars can't be compile time constants, at least in
x86 platform.
--
Hi, I'm a .signature virus, please copy/paste me to help me spread
all over the world.
Why not?
(An aside: I'd be more comfortable referring to floating-point const
*objects* rather than vars or variables; after all, the whole point is
that they aren't able to vary.)
There is, as far as I know, no context in C that *requires* a
compile-time constant of a floating-point type, other than the
initializer of a static object. Which means that the following:
static const double x = 1.5;
static const double y = x; /* ``x'' is not a constant expression */
is a constraint violation.
But given:
double x = 1.5;
why couldn't the compiler replace references to x with a constant 1.5?
--
Keith Thompson (The_Other_Keith) <ks...@mib.org>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
--
Ian Collins.
A difference between the compiler's environment and the target
environment would only make compile-time evaluation more difficult; in
itself, it wouldn't make it impossible. As long as the relevant
characteristics of the target environment are known in sufficient
detail, they can be emulated in the compiler's environment, though it
may be expensive to do so. Spending a few extra microseconds to evaluate
all floating point constants at compile time wouldn't really be noticeable.
While the standard itself provides no guarantees about the
characteristics of the target environment, there's no inherent reason
why the compiler needs to retain any doubts about what it will be. If
the environment is one where there could be any one of several different
floating point processors installed, each with significantly different
characteristics, evaluation must obviously be deferred to run-time; but
that's not the case for every environment.
Or if they can be *set* to what the compiler decided they should be.
Ian Collins wrote:
> You can't. A const variable of a non-integral type can't be used as a
> compile time constant.
Data and function pointers could/should be as well.
extern int bar1;
extern int bar2;
int *const ptr1 = &bar1;
const int *const ptr2 = &bar2;
extern void foo(int x);
void (*const funcp)(int x) = foo;
Assuming I've got the syntax correct, all three pointers
(ptr1, ptr2, and funcp) are constant pointers initialized
to the addresses of bar1, bar2, and foo, respectively.
These initializations are typically performed at program load
time prior to executing any function code.
Using them as compile-time constants is reasonable, since
their values are known uniquely at compile time and cannot
change during the lifetime of the program execution.
-drt
> extern void foo(int x);
>
> void (*const funcp)(int x) = foo;
>
> Assuming I've got the syntax correct, all three pointers
> (ptr1, ptr2, and funcp) are constant pointers initialized
> to the addresses of bar1, bar2, and foo, respectively.
> These initializations are typically performed at program load
> time prior to executing any function code.
>
> Using them as compile-time constants is reasonable, since
> their values are known uniquely at compile time and cannot
> change during the lifetime of the program execution.
>
But you just said the values are loaded at load time, so how can they be
compile time constants?
--
Ian Collins.
David R Tribble wrote:
>> Data and function pointers could/should be as well.
>>
>> extern int bar1;
>> extern int bar2;
>>
>> int *const ptr1 = &bar1;
>> const int *const ptr2 = &bar2;
>
Ian Collins wrote:
> How can these be compile time constants? The values are not known
> during the compile phase.
Yep, I knew someone would ask that.
The compiler knows the values of ptr1 and ptr2 at compile time,
which are &bar1 and &bar2, respectively. In other words, the
pointers contain unique addresses of objects defined at compile
time, irrespective of whether or not their actual bit-wise values
are known by the compiler. The compiler knows that the pointers
have unique constant values and what they are (will be) equal to
at run time.
The compile knows that the following are true at compile
time (and therefore will be true at run time):
(ptr1 == &bar1)
(ptr2 == &bar2)
Since ptr1 and ptr2 are declared as const, the compiler can
replace all occurrences of 'ptr1' and 'ptr2' with their known
equivalent const values, '&bar1' and '&bar2', during compilation,
resulting in no functional change to the resulting program.
It's almost like coding:
#define ptr1 (&bar1)
#define ptr2 (&bar2)
(with a few differences about lvalues, e.g., &ptr1).
David R Tribble wrote:
>> extern void foo(int x);
>>
>> void (*const funcp)(int x) = foo;
>>
>> Assuming I've got the syntax correct, all three pointers
>> (ptr1, ptr2, and funcp) are constant pointers initialized
>> to the addresses of bar1, bar2, and foo, respectively.
>> These initializations are typically performed at program load
>> time prior to executing any function code.
>>
>> Using them as compile-time constants is reasonable, since
>> their values are known uniquely at compile time and cannot
>> change during the lifetime of the program execution.
>
Ian Collins wrote:
> But you just said the values are loaded at load time, so how can they be
> compile time constants?
Their actual run time bit-wise values are assigned at load time,
but their symbolic object values are known uniquely at compile
time.
The compiler could, in principle, use them as any other pointer
constant:
switch (ptr1)
{
case &bar1: ...;
case &bar2: ...;
case NULL: ...;
default: ...;
}
All of the pointer values in the case clauses are known, and
known to be unique, at compile time.
-drt
I strongly recommend leaving it alone. Even a subtle change in
existing standard semantics can cause trouble for the customers.
--
Ian Collins.
Douglas A. Gwyn wrote:
> I strongly recommend leaving it alone. Even a subtle change in
> existing standard semantics can cause trouble for the customers.
The biggest benefit (IMHO) of C++ const is that it allows integer
const variables to be used for things like array sizes at compile
time:
const int N_ITEMS = 100;
float items[N_ITEMS];
This is better than using a preprocessor macro because the
constant name resides in a more limited namespace.
Likewise, allowing const variables in case expression would be
pretty useful.
Enums are a partial solution for these kinds of things, but they
cannot be assigned specific integral types, while const variables
can.
Adding this feature to C would be fairly simple, I think, and would
not break the existing semantics of const.
However, the use of external const variables (const declarations
with no initializers) would probably require obvious exclusions:
extern const N_COLORS;
float colors[N_COLORS]; // Error, unknown value
But adding anything beyond simple uses of integer const variables
(i.e., allowing them to function like more than souped-up preprocessor
macro names) is probably not wise (yet).
-drt
And because the type is specified. (Well, ``100'' also has a specific
type, namely int, but it's good to have it spelled out.)
> Likewise, allowing const variables in case expression would be
> pretty useful.
The phrase "const variables" makes me nervous. How about "const
objects"?
> Enums are a partial solution for these kinds of things, but they
> cannot be assigned specific integral types, while const variables
> can.
Yes, enums can be assigned specific values:
enum { ANSWER = 42 };
The drawback is that they can only be of type int.
> Adding this feature to C would be fairly simple, I think, and would
> not break the existing semantics of const.
>
> However, the use of external const variables (const declarations
> with no initializers) would probably require obvious exclusions:
>
> extern const N_COLORS;
>
> float colors[N_COLORS]; // Error, unknown value
>
>
> But adding anything beyond simple uses of integer const variables
> (i.e., allowing them to function like more than souped-up preprocessor
> macro names) is probably not wise (yet).
Of course, "const" would have to retain its existing meaning for
objects not initialized with a constant expression. For example, this:
const int r = rand();
is perfectly legal, and doesn't allow ``r'' to be used as a constant
expression. C++ does this.
The question is, how much code would be broken by changing the
semantics of "const" to match the C++ semantics? Does taking the
address of such a const/constant object cause problems?
--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Unless I'm missing something that exactly what David said too.
<snip>
You're absolutely right. I must have mis-read "types" as "values".
Apologies.