const or constant?


J

jacob navia

After reading some of the contrbutions to the "constant strings" thread,
I think that what is needed is a clarification of the underlying issues.

There are TWO "uses" of const:

1) Declaring that a function will not modify its arguments
2) Declaring that an object resides in ROM (read only memory)

The first one is tied to a *scope*. It says that within a certain scope,
no modifications are done to some object. I would propose that we name
this property "constant" and we would declare:

char *strchr(constant char *buffer, int searchedChar);

This means: "The strchr functions receives a buffeer that will NOT be
modified within the scope of the function". This implies that inner
scopes are required to maintain this feature. It means also that when we
exitthe scope where this "constant" declaration is done, the object
becomes writable again. Hence it is normal that strchr returns a *plain*
char pointer. "constant" objects can't be returned as constant.

The second meaning of const is the usual one and it is tied to an
*object* not a scope. It means that the object can't be modified at
*any* scope, it is an intrinsic property of the object.

In my opinion many problems with "const" would disappear if we
distinguish between these two usages of const.

Feedback welcome.
 
Ad

Advertisements

B

Bill Cunningham

jacob said:
After reading some of the contrbutions to the "constant strings"
thread, I think that what is needed is a clarification of the
underlying issues.
There are TWO "uses" of const:

1) Declaring that a function will not modify its arguments
2) Declaring that an object resides in ROM (read only memory)

The first one is tied to a *scope*. It says that within a certain
scope, no modifications are done to some object. I would propose that
we name this property "constant" and we would declare:

char *strchr(constant char *buffer, int searchedChar);

It's const not constant
 
J

Jens Thoms Toerring

jacob navia said:
After reading some of the contrbutions to the "constant strings" thread,
I think that what is needed is a clarification of the underlying issues.
There are TWO "uses" of const:
1) Declaring that a function will not modify its arguments
2) Declaring that an object resides in ROM (read only memory)

There's no requirement that a constant of type 2) is in ROM.
What about a function like

void foo( int v ) {
const int d = v;
...
}

Why should be 'd' in ROM? It only makes sure that the compiler
will get upset when you try to change its value sometime later
in the function.

And you also seem to be forgetting that 1) and 2) can be
combined as in

const char * const s

i.e. a pointer that can't be changed to a region of memory
that also can't be modified.
The first one is tied to a *scope*.

I don't see that this has anything to do with scope. The pit-
fall, I guess, is that e.g.

const char * s

doesn't mean that the value of 's' can't be changed (which
one might assume) but what it points to. I.e., for a func-
tion declared as

void bar( const char * s, const int d );

it is not really obvious for a beginner that the value of
'd' can't be changed within the function, but that of 's'
can (but not what 's' points to). It makes more sense when
one reads it out aloud as "s is a pointer to const char" -
you need to read it backwards. Reading it backword also

const char * const s

makes sense: it's a "constant pointer to const chars".
no modifications are done to some object. I would propose that we name
this property "constant" and we would declare:
char *strchr(constant char *buffer, int searchedChar);

Not only is this unfeasible (it would break millions of
programs) but it doesn't really seem to make anything
much clearer. For that you would need a different way
of declaring/defining pointer. If you could write

* char s; <=> char * s;

for "pointer to char", and

* const char s; <=> const char * s;

for "pointer to constant char", and

const * char s; <=> char * const char;

for "constant pointer to char", and, finally

const * const char s; <=> const char * const s;

for "constant pointer to const char", then you'd avoid the
necessity to read it backwards for pointers which, in my
opinion, is what can be confusing.

Another idea would be to use the "address of" operator, '&'
in declarations/definitions instead of '*', which isn't really
very logical, e.g.

&char s; <=> char * s';

would mean "s is the address of a char", i.e. a pointer to a
char, a would be nicely similar to a simple

char s;

Then you'd have

&const char s; <=> const char * s;

and

const &char s; <=> char * const s;

and, finally

const &const char s; <=> const char * const s;

That way it would make a bit more sense since it could be
read left to right and the '&' seems to be more appropriate
in this context than the '*'.

But while you're at it: perhaps you've also an idea how to
make function pointers more accessible?;-)

Regards, Jens
 
J

James Kuyper

There's no requirement that a constant of type 2) is in ROM.

Correct. It would be more accurate to say that const-qualification of an
object allows it to reside in ROM; it doesn't mandate it.
What about a function like

void foo( int v ) {
const int d = v;
...
}

Why should be 'd' in ROM? It only makes sure that the compiler
will get upset when you try to change its value sometime later
in the function.

And you also seem to be forgetting that 1) and 2) can be
combined as in

const char * const s

i.e. a pointer that can't be changed to a region of memory
that also can't be modified.

I'm not sure how you concluded that he's forgotten that; his proposal
would convert that to "constant char * const s". Note that since 'const'
would now have only the second meaning, it wouldn't have to be in any
particular position relative to the '*'. It necessarily applies only to
the object whose identifier is being declared, regardless of position.
I don't see that this has anything to do with scope. ...

The 'const' qualification within a function declaration has the first
meaning only within the scope of the identifier being declared. The
second meaning applies anywhere that an lvalue expression referring to
object can be found, whether or not the object's identifier is still in
scope. An unfortunate consequence of this fact is that violation of the
relevant restriction cannot be made a constraint violation, because the
information needed to identify it as such might reside in an entirely
different translation unit. Jacob's proposal does nothing to avoid that
problem.
 
W

Walter Banks

jacob said:
After reading some of the contrbutions to the "constant strings" thread,
I think that what is needed is a clarification of the underlying issues.

There are TWO "uses" of const:

1) Declaring that a function will not modify its arguments
2) Declaring that an object resides in ROM (read only memory)

The first one is tied to a *scope*. It says that within a certain scope,
no modifications are done to some object. I would propose that we name
this property "constant" and we would declare:

char *strchr(constant char *buffer, int searchedChar);

This means: "The strchr functions receives a buffeer that will NOT be
modified within the scope of the function". This implies that inner
scopes are required to maintain this feature. It means also that when we
exitthe scope where this "constant" declaration is done, the object
becomes writable again. Hence it is normal that strchr returns a *plain*
char pointer. "constant" objects can't be returned as constant.

The second meaning of const is the usual one and it is tied to an
*object* not a scope. It means that the object can't be modified at
*any* scope, it is an intrinsic property of the object.

In my opinion many problems with "const" would disappear if we
distinguish between these two usages of const.

Feedback welcome.

I essentially agree with you on this. It has been a big problem in
embedded systems where you want to designate that some data
resides in ROM.

The second embedded system issue is it is possible to have
volatile ROM space where the contents is not known at
compile time but is fixed at run time. An example of this is
system calibration constants programmed in ROM or
serial numbers.

w..
 
B

Ben Bacarisse

jacob navia said:
After reading some of the contrbutions to the "constant strings"
thread, I think that what is needed is a clarification of the
underlying issues.

There are TWO "uses" of const:

1) Declaring that a function will not modify its arguments

That's not well-worded. No C function can modify it's arguments (as you
know) so const can't be used to mean that. I know it seems fussy, but
if you are trying to clarify the issues, writing clearly is not a luxury.
2) Declaring that an object resides in ROM (read only memory)

I think "declaring" is the wrong word. Some implementations make take
the fact that an object is declared "const" as a prompt to try to put it
in ROM, but all you are actually declaring (in the simplest case) is
that the name of the object is not a modifiable lvalue expression.

But here are some other uses of const:

double cube_root(const double x) { ... }

const int n_interations = atoi(argv[2]);

struct atom {
const char *const name;
struct property *plist;
};

These don't fit into either of your two cases.
The first one is tied to a *scope*. It says that within a certain
scope, no modifications are done to some object.

No, it's tied to the lvalue expression. Other lvalue expressions may
permit the object to be modified in the same scope. That would
certainly be unusual but, again, you are trying to be clear here.

Given that I disagree with the dichotomy you've set up, I don't really
want to comment on the conclusions you draw. All the uses of const in C
look similar enough to be to be covered by the same keyword.

<snip>
 
Ad

Advertisements

J

jacob navia

Le 22/04/2014 23:26, Jens Thoms Toerring a écrit :
There's no requirement that a constant of type 2) is in ROM.

Well, ROM is used here as READ ONLY memory.

For instance I can obtain a ROM under windows by manipulating the page
properties and declare it read only. Under Unix there are equivalent
system calls.

Even if physically they are not part of the ROM (as the addresses in the
BIOS for instance) they *are* read only.

Of course, the compiler is free to leave that in RAM. The program should
consider those objects as read only.
 
J

jacob navia

Le 22/04/2014 23:56, James Kuyper a écrit :
The
second meaning applies anywhere that an lvalue expression referring to
object can be found, whether or not the object's identifier is still in
scope. An unfortunate consequence of this fact is that violation of the
relevant restriction cannot be made a constraint violation, because the
information needed to identify it as such might reside in an entirely
different translation unit. Jacob's proposal does nothing to avoid that
problem.

I just can't parse that, sorry. Can you provide an example?

My proposal wants to separate the usage of const to denote an
unmodifiable object, and the usage of cost to say that the object will
not be modified at the current scope but can be modified later, when it
is returned to a higher scope. That's all.
 
J

jacob navia

Le 23/04/2014 00:12, Ben Bacarisse a écrit :
That's not well-worded. No C function can modify it's arguments (as you
know) so const can't be used to mean that. I know it seems fussy, but
if you are trying to clarify the issues, writing clearly is not a luxury.

Well, if I pass a pointer (or an array) to a function, that function can
modify its arguments and const is usually not used with scalar arguments
since they are const anyway! <they are just copies of the data.

But yes, in principle you are right.
2) Declaring that an object resides in ROM (read only memory)

I think "declaring" is the wrong word. Some implementations make take
the fact that an object is declared "const" as a prompt to try to put it
in ROM, but all you are actually declaring (in the simplest case) is
that the name of the object is not a modifiable lvalue expression.

But here are some other uses of const:

double cube_root(const double x) { ... }

const int n_interations = atoi(argv[2]);

struct atom {
const char *const name;
struct property *plist;
};

These don't fit into either of your two cases.

In my opinion

double cube_root(const double x) { ... }

should be replaced with
double cube_root(constant double x) { ... }

meaning that within "cube_root" x will not be modified but outside it it
can be modified.

The second example should also be "constant" since a value obtained from
non constant data is not really const, just constant.
No, it's tied to the lvalue expression. Other lvalue expressions may
permit the object to be modified in the same scope. That would
certainly be unusual but, again, you are trying to be clear here.

Can't parse that. Can you provide an example?

An lvalue expression could be

*(p+5) = 'a';

Now what does that mean in this context?
 
J

James Kuyper

It doesn't always allow it:

const time_t now = time(NULL);

Of course that can't appear at file scope.

I've been told, on this newsgroup, that there exist systems where memory
can be switched during the run of a program from read/write to
read-only, and back again. I know nothing of the details about how that
works - but on a system where such a feature is supported, it may be
used in this case.
 
Ad

Advertisements

J

James Kuyper

That's not well-worded. No C function can modify it's arguments (as you
know) so const can't be used to mean that. I know it seems fussy, but
if you are trying to clarify the issues, writing clearly is not a luxury.

It would be more accurate to say that when a function takes a parameter
of type "pointer to const T", the function cannot modify the things the
pointer points at, except by casting away the 'const'.
It would be more useful to point out that this applies to any "pointer
to const T", regardless of whether it is a function parameter.
With those modifications, Jacob has in fact identified an aspect of
'const' that is distinct from the other aspect (though he described it
poorly).
I think "declaring" is the wrong word. Some implementations make take
the fact that an object is declared "const" as a prompt to try to put it
in ROM, but all you are actually declaring (in the simplest case) is
that the name of the object is not a modifiable lvalue expression.

No, you're also declaring that the object's value cannot be modified
with defined behavior, not even by use of an lvalue that is not
const-qualified (6.7.3p6)
But here are some other uses of const:

double cube_root(const double x) { ... }

That's an example of Jacob's second case (covered by 6.7.3p6)
const int n_interations = atoi(argv[2]);

struct atom {
const char *const name;

That is an example of Jacob's second meaning. It also is an example of
the same feature that Jacob's first meaning describes, except that it
occurs outside of a function parameter list.
 
D

David Brown

I've been told, on this newsgroup, that there exist systems where memory
can be switched during the run of a program from read/write to
read-only, and back again. I know nothing of the details about how that
works - but on a system where such a feature is supported, it may be
used in this case.

There are many ways in which memory can be mostly read-only, but
sometimes written. On "big" systems, this can be done by changing
memory mapping permissions via the MMU. On embedded systems, you get
things like flash memory with values that are unknown at compile time
(such as serial numbers that are burned in during production, and
accessed as "const volatile"), flash memory that is re-programmed at
run-time, and memory that is written during startup or configuration and
then locked in some way during normal runs.

In the case of local data, such as "const time_t now = time(NULL);", I
cannot imagine a system where this would actually be forced read-only.
In real-world systems, "now" will be either on the stack or in
registers, and these will never be read-only.
 
B

Ben Bacarisse

jacob navia said:
Le 23/04/2014 00:12, Ben Bacarisse a écrit :

Well, if I pass a pointer (or an array) to a function, that function
can modify its arguments and const is usually not used with scalar
arguments since they are const anyway! <they are just copies of the
data.

But yes, in principle you are right.

It's a common shorthand, but it really confuses many people. C's
argument passing is nice and simple: pass by value, always. I'll still
say that you "pass a string to strlen" but in a detailed discussion
about language semantics that's all wrong!
2) Declaring that an object resides in ROM (read only memory)

I think "declaring" is the wrong word. Some implementations make take
the fact that an object is declared "const" as a prompt to try to put it
in ROM, but all you are actually declaring (in the simplest case) is
that the name of the object is not a modifiable lvalue expression.

But here are some other uses of const:

double cube_root(const double x) { ... }

const int n_interations = atoi(argv[2]);

struct atom {
const char *const name;
struct property *plist;
};

These don't fit into either of your two cases.

In my opinion

double cube_root(const double x) { ... }

should be replaced with
double cube_root(constant double x) { ... }

meaning that within "cube_root" x will not be modified but outside it
it can be modified.

The second example should also be "constant" since a value obtained
from non constant data is not really const, just constant.
No, it's tied to the lvalue expression. Other lvalue expressions may
permit the object to be modified in the same scope. That would
certainly be unusual but, again, you are trying to be clear here.
Can't parse that. Can you provide an example?

An lvalue expression could be

*(p+5) = 'a';

(that's not an lvalue expression, but I know what you mean -- an lvalue
expression ('*(p+5)') is being used to modify some objecet.)
Now what does that mean in this context?

You said:

"The first one is tied to a *scope*. It says that within a certain
scope, no modifications are done to some object."

I was just making a correction. In this silly example:

static int total_count;

void show_counters(const int *counter)
{
total_count += 1;
printf("%d %d\n", *counter, total_count);
}
...

inc_counters(&total_count);

the object is modified in the scope of counter. You just made too
general a remark: no modifications are done to some object using the
pointer whose target is const qualified, but other lvalue expressions
may be able to modify it, even in the same scope. Rather that talk
about scope, I'd say your first form is a restriction on what you can do
with certain expressions. There is no guarantee that the object pointed
to won't change in any particular scope.

The confusion was significant here in that I completely missed what it
was you were saying. Sorry about that. Thanks to James's remark, I do
now see the distinction you want to make, though I am not sure you've
made a string case for enshrining it in a new keyword.

Presumably, your new 'constant' can also be used to qualify the target
of a pointer type. That would introduce new possibilities that can't
easily be written with const. void f(constant int *ip) would not be the
same as void f(const int *ip).

<snip>
 
K

Keith Thompson

David Brown said:
In the case of local data, such as "const time_t now = time(NULL);", I
cannot imagine a system where this would actually be forced read-only.
In real-world systems, "now" will be either on the stack or in
registers, and these will never be read-only.

I can certainly *imagine* it.

Given:

const time_t now = time(NULL);

any attempt to modify "now", such as:

*(time_t*)&now = -1;

has undefined behavior; that can include crashing because that memory
location has been (temporarily) marked read-only.

Doing so could be a way for an implementation to detect logical errors.
In most cases, it's going to be too expensive to be worthwhile, and I'd
be surprised to see a compiler that actually does this -- but perhaps on
some (current or future) hardware marking a memory location as read-only
is very cheap.

But the discussion was about ROM, a term that usually refers to memory
that physically cannot be modified (at least not by simply storing a
value to it).
 
K

Keith Thompson

Ben Bacarisse said:
Presumably, your new 'constant' can also be used to qualify the target
of a pointer type. That would introduce new possibilities that can't
easily be written with const. void f(constant int *ip) would not be the
same as void f(const int *ip).

I'm not entirely sure what distinction is being made (the person
making the suggestion is in my killfile), but I think adding a new
"constant" keyword in addition to the existing "const" would be a
bad way to express it.

There's already a great deal of confusion between "const" (meaning
read-only) and "constant" (meaning, in most cases, evaluated at
compile time). Adding a "constant" keyword that *also* doesn't
mean the same thing as the word "constant" as used in the standard
would just make that worse.

I offer no opinion on whether there's a useful distinction here,
worth adding a new keyword. I merely suggest using a different
keyword.
 
Ad

Advertisements

J

jacob navia

Le 23/04/2014 17:40, Keith Thompson a écrit :
the person
making the suggestion is in my killfile

At each of the threads I start, Kiki will repeat the same lie.

What is the purpose of his killfile if he participates to all the
threads I start?

BINGO!

To repeat that I am in his killfile of course!

I could forget that important fact.

Thanks kiki
 
K

Kaz Kylheku

Le 23/04/2014 17:40, Keith Thompson a écrit :

At each of the threads I start, Kiki will repeat the same lie.

How does he know whether the person making the suggestion is in his killfile?

Must be that somehow the person is there, but not the suggestion.

Ah, but the "X" in "X in my killfile" is normally understood to be a metonymy
for "X's textual output". Just like "I listen to Bach" actually means "I
listen to recordings of Bach's works", not that I have a channel into the
past that lets me listen to the man himself.

"The Usenet-published written works of that person who made the written
suggestion in a Usenet article are in my killfile. Err ..., I mean, *what*
suggestion? Someone made a suggestion?"
 
B

BartC

James Kuyper said:
On 04/22/2014 06:32 PM, Keith Thompson wrote:

I've been told, on this newsgroup, that there exist systems where memory
can be switched during the run of a program from read/write to
read-only, and back again. I know nothing of the details about how that
works - but on a system where such a feature is supported, it may be
used in this case.

(An early computer board I built had a physical toggle switch on one memory
bank, to inhibit any /write signal and thus completely protect the RAM as
though it was read-only memory, provided it was powered-up of course. (BTW
this is a more fool-proof technique than using 'const'!)

(This was to protect in-memory source code from a program going haywire; I
couldn't afford disks.)

That could also be trivially switched (with a couple of mods) from software,
although some care would be a needed, as an out-of-control program could
also re-enable the /write signal. In any case, it's not an impossibility to
take care of through software. I would, however, trust my toggle switch
rather more...)
 
Ad

Advertisements

I

Ian Collins

jacob said:
After reading some of the contrbutions to the "constant strings" thread,
I think that what is needed is a clarification of the underlying issues.

There are TWO "uses" of const:

1) Declaring that a function will not modify its arguments
2) Declaring that an object resides in ROM (read only memory)

That's what C++ constexpr supports. Why invent yet another wheel when
there is one already there?
 

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

Top