Constant strings

B

Ben Bacarisse

James Kuyper said:
That's Jacob's first meaning. This is an example of Jacob's second meaning:

const int i=5;
*(int *)&i = 6; // Undefined behavior (6.7.3p6)

That helps, thanks. His wording (referring to ROM) threw me a bit. He
wants to distinguish between the effect on expressions and the effect on
objects. There may be some benefit to making that distinction explicit,
but I don't really have a strong opinion on the matter.
 
B

Ben Bacarisse

James Kuyper said:
That's Jacob's first meaning. This is an example of Jacob's second meaning:

const int i=5;
*(int *)&i = 6; // Undefined behavior (6.7.3p6)

Further clarification... I think your example would be UB even without
6.7.3 p6 because of the rules about effective types and object accesses.
A more pointed example would have been

*(char *)&i = 6;

because that is UB only because of the const qualification on the object
named i.
 
S

Stephen Sprunk

So, const now is only being used as a kind of gentleman's agreement
between caller and callee, to make certain promises about whether the
internal data will be protected or not.

Well, I'd classify it as a guard against accidents, not malice.
However, when I try and use such a guard to suggest that a file-handling
function will not modify the file I'm passing it, I run into problems:

void dont_update_the_file(const FILE* f) {
fseek(f,0,SEEK_SET);
}

The fseek() call generates a warning. So it needs every single function,
that might take such a parameter, to cooperate with the scheme.

It generates a warning because fseek() modifies the FILE object,
specifically the current offset into the file. This is correct.

OTOH, ftell() should accept a const argument since it _doesn't_ modify
the FILE object.

Do you not see the difference?
But do people actually make use of const when working with
file-processing functions?

I've never seen it done, but I also can't think of a context where that
would make sense anyway.
If not, then they might well find that not
bothering with const works elsewhere too!

C got along okay for years without "const"; it's not critical to making
code work, but it helps find many bugs (sooner) when used correctly.

S
 
J

James Kuyper

On 04/23/2014 08:40 AM, James Kuyper wrote:
....
file, such as reading it. I think that the only operations that could,
in principle, be carried out on a FILE without modifying it are feof()
and ferror(). However, those functions are both defined as taking
"FILE*" rather than "const FILE*" parameters. That's probably because a
'const FILE*' is so nearly useless it never occurred to anyone that
there might be any point in using it for those two functions.

I should have added ftell() and fgetpos() to that list. Neither of them
takes a "const FILE*" parameter, either - but I believe that they could
have.
 
S

Stephen Sprunk

Keith Thompson said:
Do you have an example not involving string literals?

void change_array(int* a) { a[0]=9999999;}
.....
int data[]={10,20,30,40};

data[2]=rand();
change_array(data);
change_array((const int*)data);

The latter call generates a warning, the former doesn't.

You said this:

"If you think that casting a T* argument to (const T*) does anything
at all, or if you think that anyone else in this discussion has
advocated such casts, then I submit that you do not properly
understand what "const" means in C as it's currently defined."

Obviously applying such a cast can be made to do something.

Well, yes, but it's a contrived example because nobody sane would ever
cast a non-const object to const in the first place.

In the real world, most objects get their constness via the type in the
argument list: the function promises not to modify them. If such a
function then calls a function that requires a non-const argument
(because it will modify the object), the compiler will generate a
warning/error to flag that mistake. No casts required.

S
 
B

BartC

James Kuyper said:
On 04/23/2014 06:56 AM, BartC wrote:
....

It's supposed to generate a warning. That's precisely what the 'const'
is intended to achieve - preventing you from doing anything that would
modify *f. Since fseek() needs to update some of the members of the FILE
that it is passed, (specifically, the ones that keep track of the
current position in the file), the fact that a diagnostic message is
required for that call is how the 'const' achieves precisely that effect.

Your mistake lies in assuming that the 'const' protects the file managed
by the FILE object. It only protects the FILE object itself.

I don't care about modifying the FILE object. Only about the file.

If I did want a scheme to mark functions that write the to the actual files
they are passed, and those that don't, then using const might not be viable.
Since there are at least two areas that may be written to, the FILE
descriptor, and the file data itself.

The same thing can occur with other data accessed indirectly, even my image
descriptor may have attributes I want some functions to change, but I don't
want them to touch the actual pixel data.

The const attribute is too crude a feature to use reliably for this stuff. I
acknowledge it can be of use in certain cases (probably lower level and with
simpler data than my examples), but not in general.

As someone said (maybe it was you, maybe Keith), you can taking a working
program, edit all the 'const's out of it, and it should still compile and
work. That doesn't sound like a feature that is indispensible!
 
J

James Kuyper

I don't care about modifying the FILE object. Only about the file.

Too bad. 'const' only protects the former. That doesn't mean it's
useless, only that it can't be used to do what you want to do.

....
As someone said (maybe it was you, maybe Keith), you can taking a working
program, edit all the 'const's out of it, and it should still compile and
work. That doesn't sound like a feature that is indispensible!

It's a feature whose entire purpose is to mandate the generation of
diagnostic messages if certain kinds of errors are committed. Of course
it can be removed from a working program without causing any changes -
the program must have been compiled at least once without generating any
of those diagnostics, or it wouldn't be considered "working", so you can
be sure it doesn't contain any errors of those kinds.

If you are certain that you'll never write code containing the kinds of
errors that 'const' can help you detect, then you'll never need it. If
you claim that this exception applies to you, I'll assume you're wrong -
you don't even understand what 'const' does; you couldn't possibly be
relied upon to correctly evaluate whether or not you'll make a mistake
of the kind it could have helped you avoid.

It's also clearly not indispensable - C did without it for many years.
The errors it helps you discover can also be discovered by other, less
convenient methods. It's helpful, very much so, but no one has ever
claimed it's indispensable. You could re-write any C program to do the
same thing it currently does without making any use of functions (other
than main() and the C standard library), the increment or decrement
operators, compound assignment operators, the conditional operator, the
comma operator, switch statements, iteration statements, arithmetic
types other than 'int', structures or unions. None of those are
indispensable - that doesn't mean they're not useful.
 
M

Malcolm McLean

It's helpful, very much so, but no one has ever claimed it's indispensable.
It's not very helpful. It protects largely against errors you're unlikely to make in a real program.
You're unlikely to pass a read-only variable to a function which modifies it, because if the function
modifies it, normally caller will want to use that modification in some way, and people have a bit
of sense.
That's not to say there aren't a few circumstances where it will catch bugs early.
 
B

BartC

James Kuyper said:
On 04/23/2014 12:39 PM, BartC wrote:
If you are certain that you'll never write code containing the kinds of
errors that 'const' can help you detect, then you'll never need it. If
you claim that this exception applies to you, I'll assume you're wrong -
you don't even understand what 'const' does; you couldn't possibly be
relied upon to correctly evaluate whether or not you'll make a mistake
of the kind it could have helped you avoid.

I think I have a pretty good idea what it does, just questioning its
usefulness. I have thought a few times about re-implementing the same
feature elsewhere, but have never been convinced it's worth the effort.
You could re-write any C program to do the
same thing it currently does without making any use of functions (other
than main() and the C standard library), the increment or decrement
operators, compound assignment operators, the conditional operator, the
comma operator, switch statements, iteration statements, arithmetic
types other than 'int', structures or unions. None of those are
indispensable - that doesn't mean they're not useful.

const is different from any of those because:

(1) Removing it almost as easy as using global replace in a text editor (you
just have to watch out for embedded consts). All those others involve
rewriting code.

(2) The resulting code will generally be less cluttered and easier to read.
 
K

Keith Thompson

James Kuyper said:
On 04/23/2014 08:40 AM, James Kuyper wrote:
...

I should have added ftell() and fgetpos() to that list. Neither of them
takes a "const FILE*" parameter, either - but I believe that they could
have.

Quite possibly -- but the standard says so little about the contents
of a FILE object that I'm not sure making those parameters const
would have been helpful.

FILE objects are (normally) created only by calling library
functions, and are manipulated only via FILE* pointers.
An implementation could make FILE itself an integer type, an
index into a table, which would allow *all* FILE* arguments to be
defined as "const FILE *f". Another could keep additional tracking
information in the FILE object itself, so ftell() and friends might
actually update the FILE object.

A hypothetical declaration like:

long int ftell(const FILE *stream);

tells client code that the FILE object won't be modified --
but there's nothing that any portable client code can do with
that information. (And it constrains implementations, perhaps
unnecessarily.)

Opaque types don't make much of an argument either for or against
"const".

For non-opaque types, where client code actually cares about the
internals of an object, "const" can catch bugs.
 
K

Keith Thompson

BartC said:
As someone said (maybe it was you, maybe Keith), you can taking a
working program, edit all the 'const's out of it, and it should still
compile and work. That doesn't sound like a feature that is
indispensible!

It's not indispensible (C got along without it for a number of
years), but it's useful for catching errors.

Prior to the 1989 ANSI C standard, C did not distinguish between
const (read-only) and non-const (writable) data.

I've read about some oldish language, predating C, that didn't
distinguish between integer and floating-point data. It had separate
operators for integer and floating-point addition, likewise for
multiplication, et al. It was up to the programmer to keep track
of the "type" of a given variable and apply the correct operators.

C doesn't have those operators, nor does it let you declare
variables without specifying their type, but if it did, you could
take a working C program that uses both integers and floating point
and translate it to something that doesn't specify the types of
variables, and it would still work correctly. But if you modify
the resulting program, you'd have to be very careful to apply the
correct operators.

Would that be an improvement?

The distinctions C lets you make are useful, in that they let the
compiler diagnose errors for you.

What you want out of C might be different if you're generating code
by translating from some other language rather than programming
in C itself; you can do the error checking in your translator,
and nobody is expected to maintain, or probably even to read,
the generated C code. But C is designed primarily for writing and
maintaining code in C itself, and for that purpose, I like to have
the compiler do as much checking as it can.
 
K

Keith Thompson

Malcolm McLean said:
It's not very helpful. It protects largely against errors you're
unlikely to make in a real program. You're unlikely to pass a
read-only variable to a function which modifies it, because if the
function modifies it, normally caller will want to use that
modification in some way, and people have a bit of sense. That's not
to say there aren't a few circumstances where it will catch bugs
early.

You're not likely to take the square root of a string either, but I'm
glad the compiler will diagnose any attempt to do so.
 
K

Kaz Kylheku

It's not indispensible (C got along without it for a number of
years), but it's useful for catching errors.

Prior to the 1989 ANSI C standard, C did not distinguish between
const (read-only) and non-const (writable) data.

I've read about some oldish language, predating C, that didn't
distinguish between integer and floating-point data. It had separate
operators for integer and floating-point addition, likewise for
multiplication, et al. It was up to the programmer to keep track
of the "type" of a given variable and apply the correct operators.

C doesn't have those operators, nor does it let you declare
variables without specifying their type, but if it did, you could
take a working C program that uses both integers and floating point
and translate it to something that doesn't specify the types of
variables, and it would still work correctly. But if you modify
the resulting program, you'd have to be very careful to apply the
correct operators.

That is not comparable to removing const; const is not involved in
any decisions involving polymorphic dispatch. It does not provide
type inference.

In C++ it does, and so in C++ you cannot remove const from programs,
in general, without turning them into a mess that requires
a diagnostic.
Would that be an improvement?

The distinctions C lets you make are useful, in that they let the
compiler diagnose errors for you.

Except in the case of storage that is actually unmodifiable, like
string literals, const *introduces* the error conditions which it helps
diagnose.

(Again, this is different from type declarations. Type declarations do not
introduce type; type is already there, and applying the wrong type of operation
on a value is already a bug.)

A program might contain an instance of an object being modified
unitentiontally, or surprisingly.

In a program that is free of const, this doesn't require a diagnostic.

It also, in and of itself, isn't an error at the language-construct level (it
isn't undefined behavior) though it may be a software defect: one which causes
the program to compute an incorrect result over correct inputs, and perhaps
invoke undefined behavior elsewhere.

The const qualifier can help catch some instances of unintended mutations.

It helps most with those objects or object members which can be marked
as not mutable by any code at all.

When const is fastidiously applied throughout a code base (it adheres to "const
correctness") then programmers are free to assume that if a function has a
"const type *x" parameter, then the function does not modify *x. And,
conversely, if the parameter is "type *x", there is a strong reason to
suspect that the funtion does modify *x; because otherwise, in keeping
with "const correctness", it would have been declared otherwise.

This can be helpful, because it is quite important to know which functions
parameters are pure, and which mutate. If the declaration, or a comment
next to it, do not answer the question, the programmer has to waste time
investigating this.
 
J

James Kuyper

I think I have a pretty good idea what it does,

You have proven time and again, by your comments about it, that you have
some very radical misconceptions about what it does. It's not at all
clear what those misconceptions are, only that they've led to reach some
incorrect conclusions. I'm not referring to your judgements of the value
of 'const' - those are inherently subjective. I'm talking about your
(incorrect) statements of fact about the use of 'const'. The most
prominent of those were your suggestion that there was some reason why
use of 'const' would require insertion of numerous (const T*) casts.

Those misconceptions have been pointed out to you, but you've never
responded to those messages in any way that suggests that now understand
what you used to misunderstand. Nor have your responses given us any
reason to believe that your apparent misunderstanding was due to us
incorrectly interpreting what you said, nor to you expressing yourself
poorly.
const is different from any of those because:

(1) Removing it almost as easy as using global replace in a text editor (you
just have to watch out for embedded consts). All those others involve
rewriting code.

That's completely irrelevant to the value provided by 'const'. It
improves error checking - as such, the fact that it can be removed from
error-free code without changing the required behavior is no indication
that it is useless

That 'const' is easy to remove, and has no effect on the required
behavior of correct code, is also true of 'restrict' and 'register'. You
could also, in many cases, remove 'inline' and add 'static' (if it
wasn't already there) without affecting the required behavior of a
program. That doesn't mean that any of those features are useless.

It's arguably the case that 'register' is useless in modern compilers;
the last time I bothered using it was about 30 years ago. However, I
gather that when C first came out it was useful for improving the
results produced by the early compilers. The value provided by
'restrict' is at least as subtle as that of 'const', and I don't expect
you to accept that value any more easily than you accept the value of
'const'. It enables optimizations by declaring undefined behavior that
would otherwise be defined - I'd prefer that it make the corresponding
code a constraint violation, but that wouldn't be feasible in the
general case.
(2) The resulting code will generally be less cluttered and easier to read.

If you feel that way, then I recommend using such a filter when reading
C code; but not when writing or compiling it.
 
B

BartC

James Kuyper said:
On 04/23/2014 01:39 PM, BartC wrote:
I'm talking about your
(incorrect) statements of fact about the use of 'const'. The most
prominent of those were your suggestion that there was some reason why
use of 'const' would require insertion of numerous (const T*) casts.

What was wrong with that idea? It's one way of having 'const' make itself
useful, by effectively providing a write-protect guard on data being passed
to functions that might be writing to the data.

int mydata[...];

SomeFunction(mydata);

This could presumably write all over my data. But:

SomeFunction((const int*)mydata);

This will give a warning, if SomeFunction doesn't have a matching const (we
don't even need to know this). Actually I wasn't advocating doing this, just
complaining that you'd have to go to these lengths in order for const to be
useful.

Nevertheless adding this cast /will/ have that effect. Now you're saying I
am wrong about that?
That 'const' is easy to remove, and has no effect on the required
behavior of correct code, is also true of 'restrict' and 'register'. You
could also, in many cases, remove 'inline' and add 'static' (if it
wasn't already there) without affecting the required behavior of a
program. That doesn't mean that any of those features are useless.

I think static does make a significant difference to how the code will work.

Those other features used to be just compiler hints; I thought const was in
a different class to those, operating on types so that 'const T' was
distinct from 'T'.
The value provided by
'restrict' is at least as subtle as that of 'const', and I don't expect
you to accept that value any more easily than you accept the value of
'const'.

'restrict' I don't understand and don't want to. But mercifully you don't
see it in code very often, unlike const that some people like to put in at
every opportunity, something I've never had a need for.

(And C compilers don't seem to discourage over-use; the following works with
several of them:

const const const
const const const
const const const
const int const
const const const
const const const
const const const b;

somewhere in there you might see the actual type.)
 
G

glen herrmannsfeldt

(snip, someone wrote)
I don't care about modifying the FILE object. Only about the file.

Depending on buffering, it might be that only fclose() actually
modifies the file. Many people aren't good at checking the return
values from file write operations, especially fclose().

You might be surprised sometime.

-- glen
 
G

glen herrmannsfeldt

(snip)
You're not likely to take the square root of a string either,
but I'm glad the compiler will diagnose any attempt to do so.

Maybe not in C, but if it has the right value PL/I will do it.
(and in double precision, too!)

-- glen
 
I

Ian Collins

Ben said:
Ian Collins said:
BartC wrote:
This compiles as C (with gcc, Pelles C, lccwin32, DMC, gcc/TDM, and Clang
x86 compilers).

But g++ says this:

.... error: parameter 'a' includes pointer to array of unknown bound 'i32 []
{aka int []}'
static void testfn(i32 (*a)[]) {

I'm surprised gcc doesn't issue a warning about the null dimension.

(int (*)[]) and (int (*)[5]) are compatible types so there is nothing
reasonable to warn about.

As I said, it was the acceptance of "int (*)[]" as a function parameter
that surprised me. I just tried compiling the code and sure enough, gcc
silently accepts it while g++ considers it an error. The Sun compilers
issue a warning for both C and C++.
He could avoid all of the casts in C by simply not writing them!
(There's only one that I can see, and it's not needed.)

True, but he looses the array size.
 
K

Kaz Kylheku

(snip, someone wrote)


Depending on buffering, it might be that only fclose() actually
modifies the file. Many people aren't good at checking the return
values from file write operations, especially fclose().

fclose is sometimes called in inconvenient situations, like the finalization of
an object that no part of the program "cares" about any more, which happens to
hold a stream resource. A failure in fclose cannot be handled in any reasonable
way because it is a failure in an object that nobody cares about.

The trick is to flush the output while the object still matters, and perhaps
even do the fclose earlier, so that by the time the object is finalized,
the stream is already gone (indicated by a null FILE * pointer or whatever).

Anyway, if you've done fflush on the stream successfully, and fclose fails,
there probably isn't any need to take any action, so why bother checking.
At least for regular files.

On some devices, like network sockets, closing may initiate a communication
handshake which is separate from previously transmitted data. Moreover,
a fflush doesn't necessarily indicate that the data have been transmitted to
the remote host and acknowledged. A naive "close and forget" can trigger an
abort, causing not all the previously data to be transmitted, in spite of an
fflush. This isn't a problem that can be easily solved with some combination
of calls to the standard library (if you're wrapping sockets with that,
which is both possible and convenient). Basically, you need an application
level acknowledgement that everything has been received. If you have that, and
do not send any more data, you can fclose the connection and ignore the return
value.
 
B

Ben Bacarisse

Ian Collins said:
Ben said:
Ian Collins said:
BartC wrote:

This compiles as C (with gcc, Pelles C, lccwin32, DMC, gcc/TDM, and Clang
x86 compilers).

But g++ says this:

.... error: parameter 'a' includes pointer to array of unknown bound 'i32 []
{aka int []}'
static void testfn(i32 (*a)[]) {

I'm surprised gcc doesn't issue a warning about the null dimension.

(int (*)[]) and (int (*)[5]) are compatible types so there is nothing
reasonable to warn about.

As I said, it was the acceptance of "int (*)[]" as a function
parameter that surprised me.

Ah, sorry, so not the compatibility but the type itself, at least in
that context. What did you think gcc should/might complain about? It
acts, I think, just like a pointer to any other incomplete type, so I'd
be surprised if there were a complaint.
I just tried compiling the code and sure
enough, gcc silently accepts it while g++ considers it an error. The
Sun compilers issue a warning for both C and C++.

Interesting. What does it (the C compiler) warn about? A compiler can
warn about anything it likes, of course, I'm just curious to know what
it's pointing out.

<snip>
 

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,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top