Constant strings

I

Ian Collins

Ben said:
Ian Collins said:
Ben 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.

The same thing g++ considers to be an error:

"Warning: parameter type involves pointer to array of unknown size."
 
K

Keith Thompson

Ben Bacarisse said:
Ian Collins said:
Ben said:
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>

With this sample program:

#include <stdio.h>

void foo(int elems, int (*param)[]) { /* LINE 3 */
int i;
for (i = 0; i < elems; i ++) {
printf("%d%c", (*param), i == elems-1 ? '\n' : ' ');
}
}

int main(void) {
int arr[] = { 10, 20, 30, 40, 50 };
foo(5, &arr);
return 0;
}

gcc doesn't issue any warnings (even with -Wall -Wextra), but Sun C 5.9
warns:

"c.c", line 3: warning: null dimension: param

Both produce the expected output:

10 20 30 40 50
 
J

James Kuyper

What was wrong with that idea?

The idea that it's a common necessity - in particular, the idea that you
expressed, that it would have to be done almost everywhere. In almost
all contexts in programs that make proper use of 'const', there is
little or no need for such casts. Either the object itself will be
declared const, so that &identifier or array_name automatically has the
type "const T*", or conversion from "T*" to "const T*" occurs
automatically a side-effect of assignment, or of other constructs that
follow the same rules as assignment (such as passing an argument to a
function, or returning a value from a function). Assignment allows the
left operand to have more qualifiers than the right operand.
I think static does make a significant difference to how the code will work.

That's often not the case for functions declared 'inline'; it's only
such cases that I was referring to.
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'.

'const' shares a key feature with the compiler hints: it doesn't change
the required behavior to remove 'const' from code that is correct.
Adding 'const' can, in some cases, change correct code into incorrect
code; and that is a feature it also shares with the compiler hints,
(such cases are admittedly more common with 'const' than they are with
'register', 'restrict' or 'inline'). In some cases 'const' allows
certain optimizations (such as use of read-only memory), but it doesn't
mandate them.
 
B

BartC

Ian Collins said:
Ben Bacarisse wrote:

The same thing g++ considers to be an error:

"Warning: parameter type involves pointer to array of unknown size."

With C, I think its concern is that it doesn't have enough information to
increment such a pointer, for example.

But so long as I don't actually do that, why would g++ take it more
seriously?
 
S

Stephen Sprunk

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.

Why might ftell() need to "update" the FILE object? In theory, all it
needs to do is extract the current file offset and return it.

S
 
S

Stephen Sprunk

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

Your responses in this thread seem to indicate otherwise.

At minimum, you don't have a "pretty good idea" of how to use it.
const is different from any of those because:
...
(2) The resulting code will generally be less cluttered and easier to
read.

.... but more likely to contain hidden bugs.

Your main/only interest in C seems to be as an intermediate language
generated by a compiler for some other language. In that case, once you
have established your code is correct, const probably adds nothing. For
C's primary audience, i.e. humans, const is quite helpful.

S
 
K

Kaz Kylheku

With C, I think its concern is that it doesn't have enough information to
increment such a pointer, for example.

C allows pointers to incomplete types, and these are front-and-centre in a very
commonly occuring programming technique:

struct opaque;

struct opaque *opaque_thing_construct(int arg, char *other_arg);
void opaque_thing_destroy(struct opaque *);

An array of unknown size is an incomplete type. Such an object can be
declared.

struct driver device_table[]; /* in header file: incomplete array */


/* in driver.c */

struct driver device_table[] = { /* entries ... */ };

C++ is highly compatible with this.
But so long as I don't actually do that, why would g++ take it more
seriously?

It's just a warning, just like "suggest parentheses around assignment
used as a condition".
 
I

Ian Collins

Kaz said:
With C, I think its concern is that it doesn't have enough information to
increment such a pointer, for example.

C allows pointers to incomplete types, and these are front-and-centre in a very
commonly occuring programming technique:

struct opaque;

struct opaque *opaque_thing_construct(int arg, char *other_arg);
void opaque_thing_destroy(struct opaque *);

An array of unknown size is an incomplete type. Such an object can be
declared.

struct driver device_table[]; /* in header file: incomplete array */


/* in driver.c */

struct driver device_table[] = { /* entries ... */ };

C++ is highly compatible with this.
But so long as I don't actually do that, why would g++ take it more
seriously?

It's just a warning, just like "suggest parentheses around assignment
used as a condition".

That's his problem here: with g++ its an error, not a warning as expected.
 
K

Keith Thompson

BartC said:
With C, I think its concern is that it doesn't have enough information to
increment such a pointer, for example.

But so long as I don't actually do that, why would g++ take it more
seriously?

Because it's explicitly illegal in C++.

Quoting the 2011 C++ standard, section 8.3.5 [dcl.fct] paragraph 8:

If the type of a parameter includes a type of the form "pointer to
array of unknown bound of T", [...] the program is ill-formed.

C doesn't have that rule.

This is a case where valid C code is not valid C++ code -- and not one
that I've seen in any lists of such features.

If you're curious about the rationale for this restriction, I suggest
asking in one of the C++ newsgroups.
 
K

Keith Thompson

Stephen Sprunk said:
Why might ftell() need to "update" the FILE object? In theory, all it
needs to do is extract the current file offset and return it.

Just for the heck of it?

I can imagine that an implementation might want to keep track of when
the most recent operation on the file was performed; ftell() might
update a timestamp.

I don't suggest that that's particularly plausible. What is plausible
is that the authors of the standard didn't think it was worth the time
and effort to determine exactly which functions might or might not need
to modify the FILE object (at the risk of imposing unnecessary
restrictions on implementations).
 
B

Ben Bacarisse

Ian Collins said:
Ben said:
Ian Collins said:
Ben Bacarisse wrote:

BartC wrote:
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.

The same thing g++ considers to be an error:

"Warning: parameter type involves pointer to array of unknown size."

Thanks. I wonder why the authors felt the need to point that out.
 
M

Malcolm McLean

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.
You are quite likely to get the order of arguments the wrong way round for
some non-core function.
But a lot of modern languages won't diagnose the attempt to take the square
root of a string. They have dynamic typing. Sometimes strings which can be
converted to numbers will be processed as numbers, sometimes they will
throw a runtime error. I find dynamic typing harder to use, because few
real functions are genuinely generic - you have to rely on often sloppy
documentation to know what you can pass and expect back. But a lot of people
think otherwise.
 
I

Ian Collins

Malcolm said:
You are quite likely to get the order of arguments the wrong way round for
some non-core function.

Which is one case where const can save you from strange errors.
Consider memcpy for example.

I think you have just contradicted your previous argument...
 
M

Malcolm McLean

Which is one case where const can save you from strange errors.
Consider memcpy for example.

I think you have just contradicted your previous argument...
A copy function which takes two matching pointers is a case where const is
helpful, I agree. But still not all that helpful.

void copy_employee(Employee *dest, const Employee *src)

....

copy_employee(&topay, &paid);

is that call right or wrong?
 
B

BartC

Stephen Sprunk said:
On 23-Apr-14 12:39, BartC wrote:

[The meaning of 'const']
Your responses in this thread seem to indicate otherwise.

At minimum, you don't have a "pretty good idea" of how to use it.

Let's see:

(1) It really means 'read-only'

(2) It's an attribute applied to a type

(3) Such a type can be used pretty much everywhere that a non-const type can

(4) Attempts to write to an l-value with a read-only attribute raise a
compiler diagnostic, except:

(5) An object with a read-only type attribute can be initialised in its
declaration (even with a runtime value for a non-static object); and:

(6) A function parameter with a read-only type attribute can be initialised
from an argument that has non-read-only attribute

(7) Implicit coercions from non-read-only to read-only versions of the same
type ( T to const T) are allowed.

(8) Implicit coercions from read-only to non-read-only versions of the same
type (const T to T) raise a compiler diagnostic.

(9) Read-only attributes can be used by the compiler as optimisation hints,
as well helping to decide which kind of data segments static data can be
stored in.

(10) Read-only attributes can be applied to any level of any
type-specification, which means a complex type-spec can have any number of
read-only attributes (not including the multiple attributes that some
compilers allow at each level)

(11) Read-only attributes can be applied to scalars, structs and pointers,
but not to arrays.

(12) Casts can be used to convert read-only to non-read-only and vice versa

If that lot (which is is off the top of my head, so apologies if the terms
aren't quite right) isn't a 'pretty good' summary of how 'const' works, then
I will admit I have absolutely no idea what it does. But I will also then
suggest that its use is too subtle and unintuitive to be classed as a solid,
useful language feature.

I would still be interested in that case in seeing your own point-by-point
explanation of what it really does.
 
J

James Kuyper

Stephen Sprunk said:
On 23-Apr-14 12:39, BartC wrote:

[The meaning of 'const']
Your responses in this thread seem to indicate otherwise.

At minimum, you don't have a "pretty good idea" of how to use it.

Let's see:

(1) It really means 'read-only'

For an object whose type is itself const-qualified, rather than being
merely a pointer to a const-qualified type, it also means "cannot be
modified after initialization with defined behavior".
(2) It's an attribute applied to a type

(3) Such a type can be used pretty much everywhere that a non-const type can

I would say that the following item constitutes an exception to the
preceding item, and they should both therefore be re-written to
acknowledge that relationship.
(4) Attempts to write to an l-value with a read-only attribute raise a
compiler diagnostic, except:

This applies not only to explicit attempts to write to the object, but
also to certain constructs that merely put the object at risk of being
written to. The prime example of this is passing a 'const T*' to a
function where the corresponding parameter has the type 'T*'. That means
that the function could, without containing any constraint violations in
the function itself, attempt to write through that parameter - so the
function call itself has been made a constraint violation. This is
perhaps the single most useful feature enabled by 'const'.

Keep in mind that a diagnostic is required because such uses of
const-qualified types are constraint violations. That also gives an
implementation permission to reject the program. If the implementation
chooses not to reject the program, and if the user chooses to execute
the translated program, the behavior is also undefined. That's the most
seriously negative thing that C standard says about any program construct.
(5) An object with a read-only type attribute can be initialised in its
declaration (even with a runtime value for a non-static object); and:

(6) A function parameter with a read-only type attribute can be initialised
from an argument that has non-read-only attribute

(7) Implicit coercions from non-read-only to read-only versions of the same
type ( T to const T) are allowed.

(8) Implicit coercions from read-only to non-read-only versions of the same
type (const T to T) raise a compiler diagnostic.

More accurately, such conversions do not occur implicitly at all. The
diagnostic message is side-effect of the fact that the conversion did
NOT occur. It is the use of a const-qualified type in a context where it
is incompatible with the required type, that is the actual cause of the
constraint violation.
(9) Read-only attributes can be used by the compiler as optimisation hints,
as well helping to decide which kind of data segments static data can be
stored in.

That is a side-effect of the fact that the use a const-qualified types
in certain contexts leaves the behavior of the entire program undefined,
so that there is no such thing as incorrect behavior in those contexts.
An implementor is therefore free to let the behavior in those contexts
be whatever is most convenient, rather than whatever it is that the
programmer might have thought was reasonable.
(10) Read-only attributes can be applied to any level of any
type-specification, which means a complex type-spec can have any number of
read-only attributes (not including the multiple attributes that some
compilers allow at each level)

You should clarify that you're referring to levels of indirection.
'const' is a type-qualifier, and as such can only appear in locations
where type-qualifiers are allowed by the syntax, though it can be mixed
in with any of the other kinds of declaration specifiers; their relative
order has no significance. What you're referring to is the fact that
'const' has a different meaning depending upon whether it appears to the
left or to the right of any given '*' in the declaration.
(11) Read-only attributes can be applied to scalars, structs and pointers,
but not to arrays.

<nitpick>That's redundant, since pointer types are scalar types. unions
can also be declared 'const'.</nitpick>

Could you provide a citation for the array exception? Perhaps you're
thinking of the _Atomic type-qualifier, for which application to an
array type would be a constraint violation (6.7.3p2)?
(12) Casts can be used to convert read-only to non-read-only and vice versa

If that lot (which is is off the top of my head, so apologies if the terms
aren't quite right) isn't a 'pretty good' summary of how 'const' works, then
I will admit I have absolutely no idea what it does.

It is a pretty good summary; the objections I've raised are relatively
minor - if I wrote up my own summary I'd expect Keith, at least, to be
able find at least as many minor errors in my summary. He's annoyingly
good at finding such errors - but the annoyance is with myself, for
making the errors, not with him, for finding them.

Which makes it all the more confusing that you've said so many
ridiculous things about 'const' that are not justified by any of the
items from the above summary (I'm NOT referring to your subjective
judgment that 'const' is useless). If you can't use the facts you've
listed above to reach correct conclusions about how 'const' is used,
then you don't really understand those facts, even if you can list them
out correctly. Possibly your mistaken belief that use of 'const' calls
for frequent use of (const T*) casts is merely due to your deliberate
lack of experience with using 'const'.
 
G

glen herrmannsfeldt

(snip, someone wrote)
At least hypothetically, ftell might be returning a handle to an
allocated structure on systems where the notion of position in a text
file is more complex than the stream-of-bytes model*. In that case it
might be necessary to track the allocated structures so they can be
discarded when the file is closed.
*I was thinking specifically of a Q/BSAM (sequential) zOS file opened
as a text file. A position would be a combination of the output from
an NOTE macro (which effective will return the relative track and
record number), plus a position within that record. That is neither a
simple byte offset, nor small enough to fit into a long. In practice
IBM limits the usefulness of ftell to files with considerable
restrictions on size** (or at least to the beginning of large files),
and requires using ftello if you want to work with large files (and
then the off_t is large enough to hold the complete position), so this
isn't a real problem in that situation.

As well as I remember, there are four combinations to consider:
Opened as either text file or binary file, and either fixed (F or FB)
or variable (V, VB) length records. The file system keeps track of
the block, and the library the offset into the block.

I believe for FB opened in binary mode, it computes the byte offset
(BLKSIZE*relative block number + offset), and for the other
combinations, 32768*relative block number+offset, for some version
of a C library.

If you need the actual byte offset for a VB file, you have to read
from the beginning while counting the size of each block.

(The OS actually keeps track of track numbers, and blocks within
a track. An actual disk address is an eight byte value of the
form MBBCCHHR where CC is the cylinder number, HH head within the
cylinder, and R record within the track.)
**Which, depending on how the file is defined, and the release of the
OS/runtime, might be as little as a few megabytes (an unblocked file
of 80 byte records, for example, would cause ftell to fail if there's
more than about 10MB of data, at least on older versions).

The library routines could keep a table of offsets vs. block number.
and ftell() might update that table.

There is also the CMS emulation of OS files, which might work a
little differently.

-- glen
 
B

BartC

James Kuyper said:
On 04/24/2014 05:37 AM, BartC wrote:


<nitpick>That's redundant, since pointer types are scalar types. unions
can also be declared 'const'.</nitpick>

Could you provide a citation for the array exception? Perhaps you're
thinking of the _Atomic type-qualifier, for which application to an
array type would be a constraint violation (6.7.3p2)?

I can't get CDECL to accept 'const array'. That might be a bug in CDECL, but
also in the C type syntax there doesn't seem anywhere to put a const for an
array.

Maybe because 'array' doesn't really exist as an independent type, outside
of a type-specification; the type of an expression can never start with
'array', so having 'const array' would have no benefit.
Which makes it all the more confusing that you've said so many
ridiculous things about 'const' that are not justified by any of the
items from the above summary (I'm NOT referring to your subjective
judgment that 'const' is useless). If you can't use the facts you've
listed above to reach correct conclusions about how 'const' is used,
then you don't really understand those facts, even if you can list them
out correctly.

Maybe I'm simply trying to justify my own reasons for not implementing a
parallel feature elsewhere. If I can play down its usefulness (and in fact
I've never used it in my own code), then that's less work for me. But I do
think it is a minor feature that is dispensable.
 
M

Martin Shobe

I can't get CDECL to accept 'const array'. That might be a bug in CDECL,
but also in the C type syntax there doesn't seem anywhere to put a const
for an array.

Maybe because 'array' doesn't really exist as an independent type,
outside of a type-specification; the type of an expression can never
start with 'array', so having 'const array' would have no benefit.

In order to do this, you need to create a typedef. E.g.

typedef int foo_t[2];

int main()
{
foo_t const bar = { 0, 1 };

bar[0] = 2;
}
Maybe I'm simply trying to justify my own reasons for not implementing a
parallel feature elsewhere. If I can play down its usefulness (and in
fact I've never used it in my own code), then that's less work for me.
But I do think it is a minor feature that is dispensable.

One only has to look at esoteric languages like Unlambda to realize that
most of the features in production languages are dispensable. It also
shows that just because something is dispensable, it doesn't mean it
should be removed.

I'll agree that it's minor. I've worked with languages that don't have
it and survived. I've also had to track down and fix bugs that would
have been caught by the compiler had const been used correctly.
Personally, I find it to brings more value than it costs.

Martin Shobe
 
J

James Kuyper

On 04/24/2014 08:15 AM, BartC wrote:
....
I can't get CDECL to accept 'const array'. That might be a bug in CDECL, but
also in the C type syntax there doesn't seem anywhere to put a const for an
array.

What does CDECL say about

const int array[5];
 

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,773
Messages
2,569,594
Members
45,120
Latest member
ShelaWalli
Top