Netocrat said:
[D]ouble indirection can easily be used to gain access to the
supposedly protected const type by assigning the middle pointer to a
non-const pointer. There's no way the compiler can detect this, so
disallowing automatic const-conversion for double-indirection
parameters makes sense.
I'm talking nonsense. You can't assign the middle pointer to a
non-const pointer without a cast. But you can use the automatic
conversion to violate the const protection: quoting "Me" in another
thread:
const char c = 'c';
char * pc;
const char ** pcc = & pc ; /* not allowed */
*pcc = & c;
*pc = 'C'; /* would modify a const object if the conversion above were
* allowed */
Correct, but I don't think it's a good example.
I think it's a great example for what it's intended to show. What it
shows is that the const protection you are giving your c variable is
useless if a char ** variable can be automatically cast to const char **.
So really it's showing: if you want to be able to trust that the const
qualifier actually does what it's supposed to do, then automatic
conversions from char ** to const char ** _cannot_ be allowed.
It may not be as useful in explaining how this relates to your case. You
seem to be arguing that an array should be treated as though it were a
simple type, rather than as an indirected type, in this case. Actually I
don't see any problems with doing that. The trick above can't be used
when pc is declared as a char array rather than char pointer, since then
we can't change its base address. So perhaps you could argue something
like this:
Since the type char [] is fundamentally different from char * in that the
base address of the variable it defines cannot be modified, the automatic
conversion from char (*)[] to const char (*)[] is not unsafe, as the
automatic conversion from char ** to const char ** is, and should be
allowed.
I don't see any problems with this statement (others may...). But the
point is, regardless of whether this statement is true or not, it seems to
me (and again I must reiterate that I don't know what the standard says on
this) that C does treat these types equivalently for const-casting
purposes.
Simply being able to
modify something by pc and being unable to modify it by ppc is just fine,
so is in the simpler example:
char c = 'A';
char *pc = &c;
const char *pc_ = &c;
*pc = 'B'; // OK
*pc_ = 'B'; // prohibited
Yes this is all OK because the automatic conversion of &c from type
char* to type const char* can not be used in any way to surreptitiously
write to any const-protected values.
This "simpler" example is indeed just that - because it doesn't deal with
double indirection or pointers to array at all - which is the case in
point. If you really want to use a simple example that applies to our
discussion, we could change the character types to character array types
and extend it thus:
char c[5] = "abcde";
char (*pc)[5] = &c;
const char (*pc_)[5] = &c; /* warning - prohibited */
(*pc)[1] = 'B'; /* OK */
(*pc_)[1] = 'B'; /* prohibited */
This illustrates your case better. As I have argued above - to echo
what I understand to be your fundamental argument - there is no semantic
reason for prohibiting the automatic casting of &c from char (*)[5] to
const char (*)[5]. Nevertheless in C it is prohibited. C'est la vie.
Apparently the casting rules are the same as for char ** to const char **
- for which there _is_ a good reason for prohibition. We might not think
it necessary or appropriate, but that's just the way C works in this case.
Another note - // comments are only allowed by the standard in C99, and
since you didn't specifically mention using C99 I presume you are not
using switches on gcc to invoke standards-compliant behaviour at all,
which you really should. Using -ansi -pedantic makes it reject non-C90
compliant code and using -std=C99 -pedantic does same for C99. -W and
-Wall are good for additional non-standard-specific warnings.
This is how most of libc's functions are declared -- their arguments are
of type of a pointer to a constant value.
I think you mean "const-qualifed type" rather than "constant value", but I
understand you.
This is what I'd like to have
with the array too. I'm not really sure why enforcing protection in this
simple case above is OK (i.e. pc=&c
but not OK in case of using the
array as an object to be passed by reference/pointer.
Neither am I because it doesn't seem to serve any protective purpose,
but as I've tried to explain it seems to be because the array is being
treated equivalently to the case of a pointer, where a protective purpose
_is_ served.
I'm fine with
warnings and errors caused by the improper use of a pointer to a
constant object, e.g. attempt to modify the constant object. That's
perfectly understandable. Why cannot I enforce such a protection by
treating some object (not necessarily one defined as constant) as a
constant one
You can in some simple cases as your example shows, so I don't know why
you're arguing here that you cannot.
-- that's something I don't get, no matter if there's
double indirection or not.
Double indirection in the sense of char ** (but not in the sense of
char(*)[]) _does_ matter though, as the example I quoted shows.
I'm puzzled. What else am I missing?
You're totally missing the point of the example I quoted if you want to
maintain that double indirection of the form char ** should not be taken
into account when performing automatic const conversions. Double
indirection in the form char (*)[] of course is different in semantics but
apparently treated by C in the same way as char **.
Note that once we get to char (**)[] then this is a double pointer and
semantically truly does require prohibition of automatic const conversion.
Now to respond to the message you posted elsewhere:
Netocrat said:
That's true, but a pointer to an object can be treated as an array of
those objects and this is no less true for pointers to arrays - which
can be treated as arrays of arrays. So for index other than 0
ap[index] != (*ap)[index]
In fact accessing ap[1] in this instance is illegal but conceptually it
is an access of the second "5 element integer array" element of ap.
OK, I agree with this kind of double indirection.
The prob is though that we have different semantics for pointer to some
object being not an array and for pointer to some object being an array in
the sense that these two objects are treated differently.
Well that's not a totally accurate statement of the problem, because in
some cases (ie when that "some object" is a pointer) there really _is_
a need for different treatment as "Me"'s example shows. But if you
rewrite that statement as "We have different semantics for pointer to
array and for pointer to any other non-pointer type", then I agree.
There doesn't seem to be a compelling argument for pointer to array being
treated any differently to other non-simple types, such as structs. To
again modify your simple example:
struct s {
int x;
int y;
};
struct s c;
struct s *pc = &c;
const struct s *pc_ = &c; /* no prohibition here as there is when c is an
* array */
pc->x = 1; /* OK */
pc_->x = 2; /* prohibited */
If this is
because the 2nd pointer is seen as a pointer to pointer (simply because
it unfortunately points to an array)
As I've explained I believe that's the cause, but I don't know the
standard well enough to confirm this.
and we have such restrictions on
pointers to pointers,
For good reason, as the example I quoted shows.
then something is wrong in C or gcc.
That's a strong statement, and clearly as it applies to char ** conversion
its inaccurate, but as it applies to treating pointers to arrays
equivalently with double pointers, in this case your opinion that
"something is wrong in C" appears to be at the least a proposition that
could reasonably be argued.
Like I say in
the other today's post, I don't see a good reason why an attempt to
enforce treatment of some object as being constant (throught the use of
a pointer to constant) is considered bad. Let's then forget about the
array and indeed switch to a pointer to pointer:
char c = 'A';
char *p = &c;
char **pp = &p; // OK
const char **pp_ = &p; // warned
But all what type of pp_ means is that I cannot modify a char that is
somehow doubly dereferenced by pp_. And I think I must be allowed to
have this kind of assignment (pp_ = &p; in the above) because instead of
loosening the protection I try to enforce it. This is the point now.
But Alex you're now ignoring the lesson of the original example I quoted.
You're effectively saying, "the automatic conversion from char ** to
const char ** may have caused problems in Me's example, but it doesn't
in this case so it should be allowed here." Well and good, but in general
the compiler can't know when such an automatic conversion would cause
problems and when it wouldn't, which is why there is a rule that applies
in _all_ cases.
So in summary:
(a) it appears that there is no way around your problem without using a
cast. The cast doesn't appear to violate any const-protection semantics
as it would in the case where aInt3 was declared int * rather than int[5].
Casts in general are unwise though because they can mask useful (or
required) warnings, so be wary before using one.
(b) the prohibition of an automatic cast in this case does seem according
to const safety semantics to be unnecessary.