gcc: pointer to array

N

Netocrat

Al Bowers said:
If castration of const is out, do not pout. Casting, a pollution, is one
solution.


Very smart :)
(const int (*)[5])

This is even smarter. :)

So, I presume nobody knows what's wrong.

Well it's strange that you haven't had a more direct explanation as to
why the problem is occurring as yet.
I haven't found anything on this
particular thing in C99.

I'm not sure if it's in C90 or C99... I haven't looked myself but see the
thread I refer to in my other post which mentions it as being in standard
C++.
Maybe it's just gcc's way to do things... I need more statistics.

I haven't consulted the standards and don't know whether gcc is complying
with C90 or C99. But intuitively it is correct behaviour - double
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.
 
N

Netocrat

No. To the best of my understanding it is not possible according to the
standard.

Disclaimer: my knowledge of the standard is very patchy and I made this
statement based on the thread that I referenced - but looking at it again
I see that there is no actual mention of C90 or C99 - just C++ standards.
So I really don't know what the standard says about this and I'm not
motivated to go hunting through the drafts to find out. You say you've
looked and can't find anything: perhaps one of the standards gurus of this
group can clarify. It is intuitively correct behaviour though whether
standards-specified or not.
 
N

neil

Alexei said:
I have a question regarding the gcc behavior (gcc version 3.3.4).

On the following test program it emits a warning:
#include <stdio.h>

int aInt3[5] = {0,1,2,4,9};

void print2 (const int (*p)[5])
{
}

int main()
{
print2 (&aInt3); // <-- warns here
return 0;
}
The warning is:
MOD.c: In function `main':
MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type

The compiler is correct. The pointers point to incompatible types;
the element type of one array is const-qualified, and it isn't in the
other. Type compatibility is recursive, and types with different
qualifiers are not compatible.

Either the function needs to lose the const qualifier, or you need to
add it to your array.
There's another thing about the pointer to the array is... The compiler
doesn't generate a warning if I change in print2()
for (cnt=0; cnt<5; cnt++)
to
for (cnt=0; cnt<6; cnt++)
and lets me access (*p)[5], though I think at least a warning should be
generated here.

That's hard as it has to follow what your program is doing, not just
the syntax. It's certainly possible to warn, though; some lint
programs would.
It doesn't warn me if I put
aInt3[6] = 0;
into main(). But in both cases the compiler "knows" the real size of the
array, still no warning...

That's easy to warn about and GCC should do it. I suggest you file an
enhancement request.
 
O

Old Wolf

Alexei said:
I have a question regarding the gcc behavior (gcc version 3.3.4).

You got an unusually high number of garbage responses :)
On the following test program it emits a warning:
#include <stdio.h>

int aInt3[5] = {0,1,2,4,9};

void print2 (const int (*p)[5])
{
size_t cnt;
for (cnt=0; cnt<5; cnt++)
printf ("%d\n", (*p)[cnt]);
}

int main()
{
print2 (&aInt3); // <-- warns here
return 0;
}

MOD.c:: warning: passing arg 1 of `print2' from incompatible pointer type

print2 is expecting a pointer to an array of constant ints.

Note, you can't have a "const array" in C -- you can
only have an array whose members are all const.

aInt3 is an array of ints, and there is no implicit conversion
in C from pointer-to-array-of-T to pointer-to-array-of-const-T
(or an array of anything else, for that matter). This could be
considered a language defect.

But, this does work:
typedef int (*P5INT)[5];
void print2(const P5INT p)
{

AFAIK it's not possible to do this without a typedef.
 
N

Netocrat

[T]here is no implicit conversion in C from pointer-to-array-of-T to
pointer-to-array-of-const-T (or an array of anything else, for that
matter). This could be considered a language defect.

In this case it certainly would be useful. I don't see how any harm could
be done.
But, this does work:
typedef int (*P5INT)[5];
void print2(const P5INT p)
{

Not as the OP intended though. It is an equivalent prototype to:
void print2(int (*const p)[5])

So it does allow the function to be called as the OP desired - ie with a
non-const array - and without a cast - but it also removes the desired
const protection... you can now assign to (*p)[0] within the function.
The semantics of your parameter are to make p itself const, rather than
what it points to. i.e. if you add to the function:

int pp[5];
p = &pp;

you will get a warning about attempting to write to a read-only location.
AFAIK it's not possible to do this without a typedef.

It is though, as per above.
 
N

Netocrat

[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 */
 
N

Netocrat

[T]here is no implicit conversion in C from pointer-to-array-of-T to
pointer-to-array-of-const-T (or an array of anything else, for that
matter). This could be considered a language defect.

In this case it certainly would be useful. I don't see how any harm could
be done.

OK I take that back after reading another thread - harm could be done.
It's definitely not a defect but a required safety mechanism.
 
M

Maxim S. Shatskih

A pointer to an array *is* double indirection.

No. It is absolutely the same as the pointer to its first element.
 
N

Netocrat


An array is one layer of indirection. Given its similarity to a pointer I
don't see much controversy in that statement. A pointer to an array is
therefore a second layer of indirection. What do you find disagreeable
about this reasoning?
It is absolutely the same as the pointer to its first element.

What is "the pointer" to which you are referring? Do you mean the
address of its first element? In that case your statement is plainly
absurd - a pointer to an array is much more than just an address. So I
doubt that you mean that. Generally a pointer is a variable... but
we've only got one variable here, and you can't be claiming equivalence
with itself since that is true but meaningless. So what _do_ you mean?

Your statement is also vague because "first element" could be applied to
the array pointer or to the array that it points to. You don't make it
clear which you mean. i.e. given:

int (*ap)[5] = &a;

it is possible to access "it's first element" as ap[0] or (*ap)[0].

Although your statement is vague, I interpret it to be saying that

ap == &(*ap)[0]

which I don't disagree with. Since you are disagreeing with my double
indirection statement though, you perhaps would also be arguing that

ap[0] == (*ap)[0]

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. We can
make it valid by doing this:
ap = malloc(sizeof(a) * 10);
if (!ap) abort_no_mem();

Now we can write things like ap[7][4] which accesses the 5th (and last)
element of the 8th "5 element integer array" in the array of "5 element
integer arrays" which, given our ability to index a pointer as an array,
we can consider ap as representing.

Again demonstrating that a pointer to an array has two layers of
indirection. In any case I found your statement unclear so I have made
a few assumptions but I may have missed your point.
 
N

Netocrat

An array is one layer of indirection. Given its similarity to a pointer I
don't see much controversy in that statement. A pointer to an array is
therefore a second layer of indirection. What do you find disagreeable
about this reasoning?


What is "the pointer" to which you are referring? Do you mean the address
of its first element? In that case your statement is plainly absurd - a
pointer to an array is much more than just an address. So I doubt that
you mean that. Generally a pointer is a variable... but we've only got
one variable here, and you can't be claiming equivalence with itself since
that is true but meaningless. So what _do_ you mean?

Your statement is also vague because "first element" could be applied to
the array pointer or to the array that it points to. You don't make it
clear which you mean. i.e. given:

I left this out:

int a[5];
int (*ap)[5] = &a;

it is possible to access "it's first element" as ap[0] or (*ap)[0].

Although your statement is vague, I interpret it to be saying that

ap == &(*ap)[0]

which I don't disagree with. Since you are disagreeing with my double
indirection statement though, you perhaps would also be arguing that

ap[0] == (*ap)[0]

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. We can
make it valid by doing this:
ap = malloc(sizeof(a) * 10);
if (!ap) abort_no_mem();

Now we can write things like ap[7][4] which accesses the 5th (and last)
element of the 8th "5 element integer array" in the array of "5 element
integer arrays" which, given our ability to index a pointer as an array,
we can consider ap as representing.

Again demonstrating that a pointer to an array has two layers of
indirection. In any case I found your statement unclear so I have made a
few assumptions but I may have missed your point.
 
N

Netocrat

It's not. :) The values are identical, but the types aren't, strictly
speaking.

Everything else you've said is spot on, but this is slightly incorrect.
You are right that the values are identical when the array is not a
function parameter. i.e.

#include <stdio.h>
int glob_array[5];

int main(void)
{
int fn_array[5];

if ((&glob_array != glob_array) || (&fn_array != fn_array))
printf("I haven't seen a situation like this before.\n");
}

I don't know however, whether this is mandated by the standard - in
theory it isn't necessary for it to be true, but in practice it would
waste space for it not to be.

However in the context of a function parameter, it is not true. Given
that in C parameters are passed by value, it's not possible for the
address of the array parameter to equal the address of the original array
and therefore it's not possible for the address of the array parameter to
equal the address of the first element that it points to. i.e. we can
write:

#include <stdio.h>
void somefunction(int arrayparm[5])
{
if (arrayparm == &arrayparm)
printf("Something impossible has occurred.\n");
}
 
T

Tommi Johnsson

Alexei said:
Alexei said:
Yes, this would work fine if aInt3[] were declared as const array... But
the
problem is that the compiler allows pointers to consts to non-const data
but
warns on pointers to const array to non-const data. That's why I
presented
both things in the test program. Kinda odd, don't you think so? I mean,
I
can put there a type cast to get rid of the warning, but this warning in
its
very core is stupid. Why "const int*" should be better than or in any
other
way differ from "const (*int)[]"? Where's the rationale? I don't see it.
Any

i think, it's because types differs from each other...
in print1 you declare `p` as an pointer to const qualified int.
-->> pointed value is const qualified
and
in print2 you declare `p` as an pointer to const qualified array of ints
-->> pointed value is array which elements are const qualified.

i see `const int **p` more like `const int (*p)[5]` than `const int *p`
in this case, because array decays to pointer in most cases.

- jt


There were no "const int **p" in the code. Besides, "const int **p" declares
a modifiable pointer to a modifiable pointer to a constant int. There's no
double indirection anywhere in the code.

Anyway, can you suggest a declaration of the function's argument that would
simultaneously satisfy:
- being a pointer to an array of a fixed number of elements, say, 5 integers
- this array is to be constant, in other words, its elements can't be
altered by using the pointer

Can it be declared like that provided the compiler does not emit any warning
if as argument I use an array (its address) that isn't constant?

Try this one...
Array decays to pointer while array is passed to the function...

int aInt[5] = {1,2,3,4,5};

void print (const int p[5]) {
int x = 25;
if (__builtin_types_compatible_p (typeof (p), const int * ))
printf ("trallalalaa...");
printf ("%d %d %d\n", sizeof (p), sizeof (int[5]), sizeof (const
int *));
//*p[0] = 3;
printf ("%d\n", p[0]);
p = &x;
printf ("%d\n", *p);
}

int main()
{
printf ("test begin\n");

print (aInt);

printf ("test end\n");
return 0;
}


- jt
 
M

Maxim S. Shatskih

An array is one layer of indirection. Given its similarity to a pointer I
don't see much controversy in that statement. A pointer to an array is
therefore a second layer of indirection. What do you find disagreeable
about this reasoning?

The C notion of the array is another. Array is the same as pointer to its first
element (in all operations except sizeof(), and so ( p + a ) offset
operations).
What is "the pointer" to which you are referring?

MyArray and &(MyArray[0]) is the same in all contexts except sizeof().
absurd - a pointer to an array is much more than just an address.

In C and C++, it is the same.
doubt that you mean that. Generally a pointer is a variable...

Pointer is a value. Value != variable.
 
C

Chris Torek

[Given "int arr[N];" and considering "&arr" vs "&arr[0]")

Everything else you've said is spot on, but this is slightly incorrect.

Actually, it is completely correct. :) But it does not tell you
all that much by itself: as I have shown before, the values of
3 and 3.14 are identical too:

int i = 3;
double d = 3.14;

if ((char)i == (char)d)
puts("3 and 3.14 are identical");
else
puts("3 and 3.14 are different");

Obviously they are only "identical" after conversion to a common
type -- in this case "char" -- and the results may (and do) change
if we pick a different common type:

if ((float)i == (float)d)
puts("3 and 3.14 are identical");
else
puts("3 and 3.14 are different");

To compare &arr against &arr[0], we have to convert to a common
type; and as with the int/double arrangement above, this may change
the value(s) in the process:
#include <stdio.h>
int glob_array[5];

int main(void)
{
int fn_array[5];

if ((&glob_array != glob_array) || (&fn_array != fn_array))
printf("I haven't seen a situation like this before.\n");
}

If you actually compile this, you get the required diagnostic,
and in this particular case, two more diagnostics:

% cc -O2 -W -Wall -ansi -pedantic -o t t.c
t.c: In function `main':
t.c:8: warning: comparison of distinct pointer types lacks a cast
t.c:8: warning: comparison of distinct pointer types lacks a cast
t.c:10: warning: control reaches end of non-void function

(the C standard requires only that "at least one diagnostic" come
out, and does not say whether it is a "warning", or an "error", or
even a "kumquat"). We can fix this by inserting a conversion to
some common type: for instance, we could cast both to "char *".
But introducing a conversion gets us back to that 3==3.14 problem.
I think the real question boils down to whether &arr and &arr[0]
will compare equal under *all* "well-defined" conversions -- which
may even be only those to "char *" and "void *" -- and then I think
the answer is "yes", so that we can in fact say that the converted
values are always identical as long as we do a sensible conversion.

But, onward:
However in the context of a function parameter, it is not true. Given
that in C parameters are passed by value, it's not possible for the
address of the array parameter to equal the address of the original array
and therefore it's not possible for the address of the array parameter to
equal the address of the first element that it points to. i.e. we can
write:

#include <stdio.h>
void somefunction(int arrayparm[5])
{
if (arrayparm == &arrayparm)
printf("Something impossible has occurred.\n");
}

The real problem here is "what you see, well, all of it's a lie"[%].
The variable named "arrayparm" has type "pointer to int", not "array
5 of int". We can expose the lie via various operators:

/* these sizes will differ (unless you get VERY unlucky) */
printf("sizeof (int [5]) = %lu\n", (unsigned long)sizeof (int [5]));
printf("sizeof arrayparm = %lu\n", (unsigned long)sizeof arrayparm);

/* cannot do this with an array */
arrayparm = NULL;

and of course, your own example, once we insert some appropriate
conversions to eliminate the need for a diagnostic (and perhaps no
executable program as a consequence of the diagnostic):

printf("%p != %p, we presume\n", (void *)arrayparm, (void *)&arrayparm);
if ((void *)arrayparm != (void *)&arrayparm)
puts("we presumed correctly");
else
puts("uh oh");

[% "Wine from the Water", from Try Anything Once]
 
C

Chris Croughton

[Given "int arr[N];" and considering "&arr" vs "&arr[0]")

I think the real question boils down to whether &arr and &arr[0]
will compare equal under *all* "well-defined" conversions -- which
may even be only those to "char *" and "void *" -- and then I think
the answer is "yes", so that we can in fact say that the converted
values are always identical as long as we do a sensible conversion.

Is that actually defined by the standard? I remember that in some
pre-standard C compilers arrays were actually implemented as pointers,
so int arr[5]; would actually expand to the equivalent in
pseudo-assember:

arr: dw &_arr
_arr: dw ?[5]

(The array pointer itself might be declared in a read-only segment.)

Is this sort of expansion actually banned by the standard, or is it
"just not the done thing"?

In those compilers the effect would be the same as happens with "array
parameters", taking &arr would get you the address of the pointer.

Chris C
 
L

Lawrence Kirby

Alexei said:
Yes, this would work fine if aInt3[] were declared as const array... But the
problem is that the compiler allows pointers to consts to non-const data but
warns on pointers to const array to non-const data. That's why I presented
both things in the test program. Kinda odd, don't you think so? I mean, I
can put there a type cast to get rid of the warning, but this warning in its
very core is stupid. Why "const int*" should be better than or in any other
way differ from "const (*int)[]"? Where's the rationale? I don't see it. Any
idea?

An array of ints is a fundamentally different type of object to a an int.
So a pointer to an array of ints is quite different to a pointer to an
int, because the properties of a pointer depend very much on the type of
thing they point at.
i think, it's because types differs from each other...
in print1 you declare `p` as an pointer to const qualified int.
-->> pointed value is const qualified
and
in print2 you declare `p` as an pointer to const qualified array of ints
-->> pointed value is array which elements are const qualified.

i see `const int **p` more like `const int (*p)[5]` than `const int *p`
in this case, because array decays to pointer in most cases.

But this is an important example of where that is not the case. In
const int (*p)[5] p is already a pointer, it cannot "decay" further. This
array -> pointer conversion is only applicable at the top level of a type.
It is important to remmeber that arrays and pointers really are
fundamentally different types, although their operation is naturally
connected.

Lawrence
 
A

Andrey Tarasevich

Alexei said:
...
So, I presume nobody knows what's wrong. I haven't found anything on this
particular thing in C99. Maybe it's just gcc's way to do things... I need
more statistics.
...

What's wrong in the original case is perfectly clear here. The warning is a
consequence of C language's const-correctness rules when they are applied to
arrays (and it's been mentioned here already).

Simply speaking, in accordance with C language's const correctness rules, you
can perform an implicit 'T* -> const T*' conversion. Often people assume that,
say, 'int (*)[10] -> const int (*)[10]' conversion also belongs to that category
and should be performed implicitly without any warnings (that's what you
assumed, apparently). Unfortunately, this is not the case. In C language it is
not possible to const-qualify an array type as a whole. Period. Any attempts to
const-qualify an array type will actually const-qualify the type of the array
_elements_ not the array type itself. In other words, there's no situation in C
when the 'T* -> const T*' can be applied to pointers-to-arrays, since the proper
destination type cannot ever exist in C.

Best regards,
Andrey Tarasevich
 
T

Tommi Johnsson

Lawrence said:
Alexei said:
Yes, this would work fine if aInt3[] were declared as const array... But the
problem is that the compiler allows pointers to consts to non-const data but
warns on pointers to const array to non-const data. That's why I presented
both things in the test program. Kinda odd, don't you think so? I mean, I
can put there a type cast to get rid of the warning, but this warning in its
very core is stupid. Why "const int*" should be better than or in any other
way differ from "const (*int)[]"? Where's the rationale? I don't see it. Any
idea?


An array of ints is a fundamentally different type of object to a an int.
So a pointer to an array of ints is quite different to a pointer to an
int, because the properties of a pointer depend very much on the type of
thing they point at.

i think, it's because types differs from each other...
in print1 you declare `p` as an pointer to const qualified int.
-->> pointed value is const qualified
and
in print2 you declare `p` as an pointer to const qualified array of ints
-->> pointed value is array which elements are const qualified.

i see `const int **p` more like `const int (*p)[5]` than `const int *p`
in this case, because array decays to pointer in most cases.


But this is an important example of where that is not the case. In
const int (*p)[5] p is already a pointer, it cannot "decay" further.

in declaration like const int (*p)[5] it declares only type for p it
really doesnt decay anything but above in 1.st and 2.nd declaration of
pointer p, p is needed to indirect before using p pointed value as an
array, in 3.rd declaration there is no need for that and in later use
p pointed array will decay to the pointer when indirected throught it, i
suppose.

- jt
 
A

Alexei A. Frounze

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. 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
This is how most of libc's functions are declared -- their arguments are of
type of a pointer to a constant value.
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.
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 --
that's something I don't get, no matter if there's double indirection or
not. I'm puzzled. What else am I missing?

Alex
 

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,780
Messages
2,569,608
Members
45,248
Latest member
MagdalenaB

Latest Threads

Top