Netocrat said:
Netocrat said:
On Mon, 11 Jul 2005 09:29:27 +1000, Netocrat wrote:
[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.
The thing about the base address is that I can use any index/offset to get
outside the allowed memory region, be it array or a pointer, I can simply
write:
ptr[123456] = 0; or
arr[-987654] = 1;
whatever. C will let me shoot in the foot
As I showed, gcc doesn't warn
about writing past the array's last element. At least, it did not do so with
the -Wall option, which I normally use.
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].
Yep.
Nevertheless in C it is prohibited. C'est la vie.
Sh!t.
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.
Well, there used to be a lot of nonstandard (not yet standardized) features,
among which // is more or less harmless. I do not know many good compilers
that do not support // but nevertheless are of some good value to the
developers.
But let's not get away from the topic for now.
I think you mean "const-qualifed type" rather than "constant value", but I
understand you.
Well, I may not be expressing myself well in terms of C's jargon, but I hope
I write clearly enough to be understood even though I'm not speaking English
natively
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.
OK, how about:
const char* * const p
?
Would it be better than simply
const char** p
?
Sorry, I maybe talking nonsense at the moment... But if the dereferenced
character is protected by const (or is it not?), then I'm not sure of the
problem, the write/assignment should be prohibited.
You can in some simple cases as your example shows, so I don't know why
you're arguing here that you cannot.
I cannot with arrays.
....
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.
Doesn't matter whether you say pointer or array. We both see the same
problem with [const]char** and [const]int(*)[]. So, yes, we do have
different semantics... To me its more of an inconsistency. C is a great
language, but it really does have some problems and peculiarities someone
should be aware of (say, two ints are multipled, and the product is assigned
to a long. what a normal human being expects to get isn't something the
experienced programmer knows to get in reality).
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 */
Sure. Now I think what if I typdef or union the array with something else to
hide the array type... Would I still get the warning? Or would I get a
different one? Sure this is something stupid to do, but what the heck, since
we're into, there are maybe some posibilities
As I've explained I believe that's the cause, but I don't know the
standard well enough to confirm this.
For good reason, as the example I quoted shows.
Can you show *the bad* code, where this automatic pointer type conversion
(if considered to be fully allowed) yields something real nasty? I'm getting
lost...
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.
Maybe...
But Alex you're now ignoring the lesson of the original example I quoted.
I'm lost. That's it.
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."
I might missed that "Me" example you're referring to...
Wait! This one:
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 */
Shoot. I didn't read it with enough attention. I see it now.
But ... what a perverted mind one must have to do something like that!
Cetainly, that wasn't something I expected to see nor do myself!!! At least,
not now, having programmed in C for quite some years.
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.
A big thanks,
Alex
P.S. where was I when I saw that weird example, huh?