const qualifier

M

Marc van Dongen

Dear all,


I'm a bit confused about the use of the const qualifier. I've studied
the ANSI version of K&R but this does not seem to provide an
explanation which corresponds to how const may be used (and is used).

E.g. it seems that (according to gcc) both "const int var" and "int
const var" are the same. Likewise, "const int *var"and "int const *
var"seem to mean the same. Writing "int * const **var" seems to mean
that the "int *" is constant.

I'd appreciate it if somebody could point me to some (preferably on-
line) documentation which describes exactly how to use const.

TIA.

Marc van Dongen
 
M

Malcolm McLean

Marc van Dongen said:
I'm a bit confused about the use of the const qualifier. I've studied
the ANSI version of K&R but this does not seem to provide an
explanation which corresponds to how const may be used (and is
used).

E.g. it seems that (according to gcc) both "const int var" and "int
const var" are the same. Likewise, "const int *var"and "int const *
var"seem to mean the same. Writing "int * const **var" seems to
mean that the "int *" is constant.

I'd appreciate it if somebody could point me to some (preferably on-
line) documentation which describes exactly how to use const.
Basically it means "This function does not modify the thing pointed to by
this pointer"

eg

strcpy(char *dest, const char *src)

dest must be writeable, src need not be - it could point to an area of ROM,
for instance.

Unfortunately the keyword is broken. There are no sensible rules on what
should happen if dest and src overlap, for example. Also, if we've got this

struct employee
{
char *name;
float salary
};

foo(const struct employee *minion)
{
strcpy(minion->name, "Fred");
}

is legal. The constness only applies to the structure itself.

Finally we've got this problem

strstr() should be able to search for a substring in a constant string, of
course. But also in a string in RAM that you want to modify. So if passed a
constant it should return a co0nst char *, if a variable a char *, since
often the reason you want the address of the substring is to modify it.
There's no way of expressing this in C.

My advice is to write code without consts, then add them just as often as
needed to shut the compiler up.
 
J

Jean-Marc Bourguet

Marc van Dongen said:
Dear all,


I'm a bit confused about the use of the const qualifier. I've studied
the ANSI version of K&R but this does not seem to provide an
explanation which corresponds to how const may be used (and is used).

E.g. it seems that (according to gcc) both "const int var" and "int
const var" are the same.

They are.
Likewise, "const int *var"and "int const *
var"seem to mean the same.
Right
Writing "int * const **var" seems to mean
that the "int *" is constant.
Correct.

I'd appreciate it if somebody could point me to some (preferably on-
line) documentation which describes exactly how to use const.

const applies to what preceed it. If nothing preceed it, it applies to
what immediately follows.

Yours,
 
M

Marc van Dongen

[problem with const]

Basically it means "This function does not modify the thing pointed to by
this pointer"

eg

strcpy(char *dest, const char *src)
dest must be writeable, src need not be - it could point to an area of ROM,
for instance.

Thanks. I'm aware of this:)

[problems]

I'd like to know how to parse a type with const qualifiers in them.
Regarding the qualifiers as operators, I'd like to know how they bind
to the ``read-onlyness of the types.'' So int *const ** var means that
the int * is read-only. Here it is the entire type to the left of the
const. However, when we write const int *var, it means some part of
the type to the right is read-only(the int). If I were to write a
compiler I'd have to know the rules. My question is "What are the
rules?".

Regards,


Marc van Dongen
 
M

Marc van Dongen

[snip]
I'd appreciate it if somebody could point me to some (preferably on-
line) documentation which describes exactly how to use const.

const applies to what preceed it. If nothing preceed it, it applies to
what immediately follows.

Thank you. That seems to be what I figured out by trial and error. Is
this written down somewhere?

Regards,


Marc van Dongen
 
J

James Kuyper

Marc said:
Dear all,


I'm a bit confused about the use of the const qualifier. I've studied
the ANSI version of K&R but this does not seem to provide an
explanation which corresponds to how const may be used (and is used).

E.g. it seems that (according to gcc) both "const int var" and "int
const var" are the same. Likewise, "const int *var"and "int const *
var"seem to mean the same. Writing "int * const **var" seems to mean
that the "int *" is constant.

Qualifiers like 'const', 'volatile', and 'restrict' can be freely mixed
with type names like int or short or float of struct tm, without
changing the meaning of the declaration. However, the order of
qualifiers and '*' does matter. In the declaration;

int * const cpi

The 'const' qualifier applies to 'cpi' itself. In the declaration

int const * pci

the 'const' qualifier applies to the thing pointed at by pci.
I'd appreciate it if somebody could point me to some (preferably on-
line) documentation which describes exactly how to use const.

That is a big, complicated subject, and I don't know of a good online
tutorial. My summary:

Declaring something const makes it a constraint violation to attempt to
modify it. This is a good thing, because implementations are required to
diagnose constraint violations. If you can defer declaring an object
until it already has it's initial value, and if you know that the
initial value should never be changed, then it's a good idea to declare
it const, because that will ensure that you get diagnostic messages if
you made a mistake, and your code actually changes the value of the
object. You might need to fix the code which changes the value, or you
might need to remove the 'const' qualifier, but the diagnostic is useful
because it points out to you the conflict between what your code does
and what you thought it should do.

You can usually work around the restrictions by using a cast:

*(int *)pci = 3;

This is perfectly legitimate in itself (but see below), and there are
sometimes legitimate reasons for doing so. However, you should
understand that whenever you use an cast, you're telling the compiler
"This looks bad, but I know what I'm doing". Always be very sure that
you do indeed know what you're doing, before using any explicit cast.
Treat any cast you find in anyone's code, including your own, as a
danger sign.

Defining an object to be 'const' is a more complicated issue from simply
declaring it. The result is that if your code uses the above technique
to attempt to modify the value of that object, the behavior of your
program is undefined. The change might simply fail to happen, or your
program might abort(), but there's a literal infinity of other possible
behaviors that are generally less likely than those two.

On some platforms, it's possible for your compiler to lock a piece of
memory against changes after initializing it. If so, the 'const'
qualifier on an object definition gives the compiler permission to do
so. That's an easy way to think about it, and I recommend keeping that
model in mind. However, even on machines where there's no such
capability, an implementation is still permitted to generate code based
upon the assumption that such an object's value will never be changed.
As a result, arbitrarily bizarre things can happen if that assumption is
violated. For instance:

In one part of the code:
const int i=5;

In another part of the code:
int j = 10*sizeof(int);

Instead of loading an actual value of 10 into a register, a conforming
implementation could load the current value of 'i' and multiply by 2.
If you've changed the value of 'i', that will result in an invalid value
being given to j, despite the fact that there's no obvious connection
between them. That's an extremely unlikely way of handling such code,
but it's no less dangerous than other more likely possibilities that are
harder to explain.
 
B

Ben Bacarisse

Marc van Dongen said:
I'd like to know how to parse a type with const qualifiers in them.
Regarding the qualifiers as operators, I'd like to know how they bind
to the ``read-onlyness of the types.'' So int *const ** var means that
the int * is read-only. Here it is the entire type to the left of the
const. However, when we write const int *var, it means some part of
the type to the right is read-only(the int). If I were to write a
compiler I'd have to know the rules. My question is "What are the
rules?".

The rules are in the formal syntax. This does not really help except
to act as the final arbiter. The syntax is messy enough that hand
parsing is tedious. So, just like the syntax for expressions has been
synthesised into a precedence table, the type syntax rules have been
synthesised into various "rules of thumb".

What I used to tell students was:

Re-write so you have only one "thing" being declared/defined, then put
your finger on the name (or where the name would be) and read out the
name followed by the symbols using these rules:

* Always read to the left first but never past a ')'.

* Then read to the right.

* If you hit the start you are done.

* If you hit a '(' remove everything in the matching '(...)' pair
and start again reading to the left.

As you do this voice the symbols like this:

'*' "pointer to"

'[' "array of" then read what is inside the '[]'

'(' "function taking" and then read using these rules each of the
parameter types (you need to jump to find the where the name
is or would be in each one). For '(void)' just say "no
parameters", and for '()' say "unspecified parameters".
Follow this with "and which returns" and keep going.

'...' "further var-args" (you can re-word this if it too much like
Vogon poetry for you).

You have to add in a few extra words to make real sentences but the
basic idea of going from the inside out and to the left first is a
basic consequence of the formal syntax of the language.

Unfortunately this is not 100% "culture fair" since it relies on a
property of English that "int const" and "const int" probably indicate
the same thing. I have found some people really bothered by this and
they are usually non-native English speakers.

If fails for function parameters of the form 'x[..]'. You can update
the rules to take these into account but it is a bit messy.

A couple of examples:

const struct node *f(void);
^
start here

"f is a function taking no parameters and which returns a node
struct const."

int (*list_op)(const struct node *);

"list_op is a pointer to a function taking a pointer to a node
struct const and which returns an int."
 
J

jameskuyper

James Kuyper wrote:
....
Qualifiers like 'const', 'volatile', and 'restrict' can be freely mixed
with type names like int or short or float of struct tm, without

"mixed" was the wrong term. What I meant is that they can freely be
reordered with respect to each other.
 

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