const type qualifier and external linkage (term.?)

B

Ben Petering

Hi group,

this is a 'best practice' type question (I want discussion of the issue
- whys and why nots - similar to "casting the return value of malloc()",
to cite an analogous case).

Let's say I have a function written in assembler/(some non-C language)
which takes one pointer-to-const-char arg, the declaration being:

extern unsigned int str_len(const char *s);

Is the 'const' type qualifier 1) advisable and 2) legal? The C compiler
has no way of enforcing what a non-C function does with the pointer, and
as I understand it, the type qualifier is a guarantee of sorts - the
compiler guarantees said l-value will not be modified using the passed
pointer.

If I'm not mistaken, this makes the presence of the qualifier
inadvisable. Correct?

Response appreciated. Apologies for possible OT or incorrect terminology.

-ben
 
R

Richard Heathfield

Ben Petering said:
Hi group,

this is a 'best practice' type question (I want discussion of the issue
- whys and why nots - similar to "casting the return value of malloc()",
to cite an analogous case).

Let's say I have a function written in assembler/(some non-C language)
which takes one pointer-to-const-char arg, the declaration being:

extern unsigned int str_len(const char *s);

Is the 'const' type qualifier 1) advisable and 2) legal?

Well, it's certainly legal. Whether it's advisable is less obvious. But
think of it this way. Firstly, if the assembly language code changes the
value of data pointed to by s, then the const is a lie and should be
dropped. Having established that it doesn't, you then have the problem of
maintenance people unwittingly modifying the string within the assembly
language routine. You can go some way towards solving this via
documentation:

loadaddr r0, s ; Do not modify the chars in this string!

but it's not a guarantee, of course.

It might be wiser to leave the const /off/ the declaration, rather than
make a promise that might be broken later, but bear in mind that this
might result in conscientious programmers only sending data copies to the
function, rather than data originals - with an obvious potential for
impact on performance.
 
C

Chris Torek

this is a 'best practice' type question (I want discussion of the issue
- whys and why nots - similar to "casting the return value of malloc()",
to cite an analogous case).

Let's say I have a function written in assembler/(some non-C language)
which takes one pointer-to-const-char arg, the declaration being:

extern unsigned int str_len(const char *s);

Is the 'const' type qualifier 1) advisable and 2) legal?

It is definitely "legal" (a poorly-specified term, but no matter how
*you* are using it, I think it comes out the same ... of course it is
possible that none of the interpretations I am guessing at are the one
you intended). As for "advisable", that depends.
The C compiler has no way of enforcing what a non-C function does with
the pointer,

Indeed -- but it has no way of enforcing what a C function does either:

void icky(const int *p) {
*(int *)p = 42;
}

is valid C code, with no diagnostic required. I would call this
"inadvisable" though. :)
and as I understand it, the type qualifier is a guarantee of sorts - the
compiler guarantees said l-value will not be modified using the passed
pointer.

No, there is no such guarantee (see icky() above). It does tend
to promise a human reader that the function will not modify *s,
though. The main thing that it (this const) does, in a C compiler,
is make calls that pass const-qualified pointers semantically
correct, so that no diagnostic is required.

I think this is far clearer when shown by example. Suppose we are
writing some C code and are working on some intermediate or top level
function:

void something(const char *str) {
unsigned int val;

... do some work ...
val = str_len(str); /* note this line */
... do more work ...
}

For whatever reason (good or bad) our function, something() in this
case, takes a "const char *" value. It then calls the assembly
str_len(), passing the pointer value "str" on. If you change the
declaration for str_len() to take plain (non-const-qualified)
"char *", the call at the "noted" line now violates a constraint,
and a diagnostic is required.

If something() is an intermediate-level function, and we want to
call it as follows, then having something() take "const char *" is
"good" because one of the following calls provides a pointer to
"const char":

const char const_buf[] = "hello";
char modifiable_buf[] = "world";
...
something(const_buf);
something(modifiable_buf);

It is OK to "add" a const qualifier (but only a "top level" one;
see the comp.lang.c FAQ for details; C gets this wrong while C++
gets it right) in a call, but it is not OK to remove one. This is
(presumably) why something() takes a "const char *", and if
something() needs to call str_len(), and str_len() will not modify
the chars involved, including the qualifier in the declaration for
str_len() is probably a good idea.
 
T

Thad Smith

Richard said:
Ben Petering said:

Well, it's certainly legal. Whether it's advisable is less obvious. But
think of it this way. Firstly, if the assembly language code changes the
value of data pointed to by s, then the const is a lie and should be
dropped. Having established that it doesn't, you then have the problem of
maintenance people unwittingly modifying the string within the assembly
language routine. You can go some way towards solving this via
documentation:

loadaddr r0, s ; Do not modify the chars in this string!

but it's not a guarantee, of course.
Agreed.

It might be wiser to leave the const /off/ the declaration, rather than
make a promise that might be broken later, but bear in mind that this
might result in conscientious programmers only sending data copies to the
function, rather than data originals - with an obvious potential for
impact on performance.

I disagree. The declared interface, to me, is a contract. Changing the
functionality of the function without correspondingly updating the
interface is a no-no in my book. Further, if a coder changes the
functionality and not the name, it is his responsibility to
1) update the interface declaration and description
2) ensure that all required changes in any calling code or outside
documentation are made (when I change functionality, I often change the
function name to be on the safe side).

My recommendation if there is no intent to change the pointed-to argument
is to declare the argument const, then ensure you fulfill that promise. It
seems silly, to me, to make the prototype less accurate because the code
may be later changed by an uncaring oaf.

YMMV
 
T

Thad Smith

Chris said:
Indeed -- but it has no way of enforcing what a C function does either:

void icky(const int *p) {
*(int *)p = 42;
}

is valid C code, with no diagnostic required. I would call this
"inadvisable" though. :)

Even
int x = (int*)p;
would result in undefined behavior if p pointed to an object declared
const. I am aware of one implementation in which the results of
int x = (int*)p; and
int x = *p;
can be different for parameter const int *p.
 
H

Harald van Dijk

Even
int x = (int*)p;

I'll assume you meant *(int*)p.
would result in undefined behavior if p pointed to an object declared
const. I am aware of one implementation in which the results of
int x = (int*)p; and
int x = *p;
can be different for parameter const int *p.

Accessing read-only memory using non-const pointers is certainly allowed
in C and in fact quite often done, since string literals have type (non-
const) char[]. It's also valid for objects defined explicitly as const;
there's simply no prohibition against doing so. The behaviour is
undefined if you try to write to read-only data (whether string literal
or const). Could you give some details about your implementation?
 
R

Randy Howard

I disagree. The declared interface, to me, is a contract. Changing the
functionality of the function without correspondingly updating the
interface is a no-no in my book. Further, if a coder changes the
functionality and not the name, it is his responsibility to
1) update the interface declaration and description
2) ensure that all required changes in any calling code or outside
documentation are made (when I change functionality, I often change the
function name to be on the safe side).

Yes, in a perfect world, interface changes would be minimized, and when
they did occur, it would documented and everyone with code depending
upon the change would be magically notified. One example of this sort
of thing is the swath of win32 DoesSomethingFamiliarEx() functions,
where they tack on two letters, completely change the interface, and
expect you to go back through old code and make the changes. It's
ugly, but at least they have documentation.

I think this crops up primarily with so-called "const poisoning",
because many programmers are far from convinced of it being worthwhile.
Perhaps that is because it is "new" and, old dogs, new tricks, etc.
Perhaps it is because it can't be guaranteed, and that makes
experienced programmers suspicious.

Programmers seem to be less suspicious about "scarier" interfaces,
where you pass a void pointer in and /hope/ that the right thing is
done with it, which is at least ironic, given the potential
consequences there.
 
T

Thad Smith

Harald said:
I'll assume you meant *(int*)p.

Yes. Thanks for catching this.
would result in undefined behavior if p pointed to an object declared
const. I am aware of one implementation in which the results of
int x = (int*)p; and
int x = *p;
can be different for parameter const int *p.

Accessing read-only memory using non-const pointers is certainly allowed
in C and in fact quite often done, since string literals have type (non-
const) char[]. It's also valid for objects defined explicitly as const;
there's simply no prohibition against doing so.

Oops, I misread the Standard. 6.7.3 p5 switched from discussing const
modifier to volatile modifier and left me with the wrong impression.
The behaviour is
undefined if you try to write to read-only data (whether string literal
or const). Could you give some details about your implementation?

A different addressing mechanism is used for read-mostly (flash) memory and
read-write memory (RAM). const variables are placed in flash memory.
Dereferencing a non-const pointer uses only RAM access while dereferencing
a const-qualified pointer does run-time testing to access either RAM or
flash. There is no language mechanism to write const variables. I believe
there is a compile-time option to place string literals in read-mostly memory.
 

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,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top