[again my apologies for a much delayed response]
Harald van Dijk said:
On 2011-07-08, Harald van D??k <
[email protected]> wrote:
[ int a[4][5];
int *p1 = &a[0][0];
int *p2 = (int *) &a; ]
Is the conversion of &a to int * is guaranteed to point to a[0][0]?
Looking deeper into it, I see a lot that isn't, strictly speaking,
guaranteed by the standard, but will work on any sane implementation.
Not even malloc is guaranteed to be useful:
int *a = malloc(sizeof *a);
The result of the conversion from void * to int * is not specified as
pointing to the allocated memory, as far as I can tell. [snip]
Did you read 7.20.3p1?
Thanks, I missed that. By putting it there, it only applies to (c|m|
re)alloc, as an exception to the general rules for pointer conversions
as defined in 6.3 "Conversions". It does not apply to other
conversions from void * to T *, so unless part of a T * -> void * ->
T* conversion sequence, is the behaviour of such a conversion defined?
I believe it is, yes.
Consider a conversion sequence T * -> void * -> char * -> void * -> T
*, which happens with a custom implementation of qsort or similar
functions.
That particular conversion sequence actually is guaranteed fairly
directly by 6.3.2.3. But I assume you mean to ask a more subtle
question.
Disclaimer: I'm not suggesting that relying on that conversion is a
bad idea. I'm saying the standard fails to state that relying on that
conversion is valid.
It doesn't say it as directly as some people would like. Different
people make different assumptions about how the Standard should be
interpreted. My best understanding is, reading the Standard as WG14
intends or expects it to be interpreted, the Standard actually does
guarantee that pointer conversions work the way most (informed)
people expect them to work. (More details below...)
Besides, the description of the + operator doesn't talk about
contiguous memory, it talks about array subscripts. p2 + 7 is valid if
p2 points to an array of length 20. The only array of int that starts
at (int *) &a -- if the result of the conversion is defined -- is
a[0], which has a length of no more than 5.
Doesn't matter, so far as I can tell. If you malloc sizeof(int)*4*5 items,
The rules concerning objects' types are different for dynamically
allocated memory, and it is unclear to me how exactly the rules are
meant to interact with arrays.
Objects don't have types. Objects have an /effective type/ for
the purposes of a particular access, but objects do not have a
type.
Right, that was badly worded. It is the effective type that is handled
differently for dynamically allocated memory.
The notion of effective type is irrelevant to these questions
about pointer conversion and array indexing. By the time an
access happens, all the pointer conversion and indexing
arithmetic has been done; as long as the ultimate element
type matches, if the conversions and indexings worked then
the access works -- and effective type doesn't enter into
the stipulations for pointer conversions or array indexing.
If E is an lvalue such that &E is legal, we may always
construct '(unsigned char (*)[ sizeof (E) ]) &E'. Do you agree?
No, but I think it's not relevant to this discussion.
Right, I should have mentioned the condition about alignment,
which was meant to be implied.
I agree that we
may always construct (unsigned char *) &E, and believe that serves
your point just as well.
Probably you're right; I thought putting in the size
explicitly made the point more clearly.
There is a special exception for character types, but can you show the
support this for any other type?
I will ask a question: do you believe the Standard defines
"pointers" or "pointer values" in such a way that valid pointers
(that aren't null) point to some object? I believe it does -
pointers (even pointers of type (void*)) don't point "nowhere", they
point at something (as before assuming they aren't null or don't
have alignment problems). The behavior of various pointer
conversions follows from this implication.
I know some people read some sections of 6.3.2.3 (notably, for
example, the third sentence of 6.3.2.3p7) as limiting how pointer
conversions work. IMO that interpretation is incorrect; these
sentences are meant to add to what is allowed for pointer
conversions, not take away from them. For example, consider this
fragment:
int *p0 = malloc( sizeof *p0 );
long *p1 = (long*) p0;
int *p2 = (int*) p1;
// now p2 == p0 must be true, even if sizeof (long) > sizeof (int)
We know all these conversions are legal just by the first sentence
of 6.3.2.3p7 (as usual assuming no alignment problems (if indeed
any assumption is actually needed, because of calling malloc())).
However, there is some question about what the conversions do,
because the object actually allocated may not be large enough to
accommodate a (long) object. The third sentence of 6.3.2.3p7
means the conversions have to work the way we expect even in this
case, where the object size actually available isn't large enough
for the pointer type in question. When the object size /is/ large
enough for the converted-to pointer type, then the pointer also
has to work for purposes of dererencing (again assuming no
alignment issues, and no violations of effective type rules).
This conclusion follows from the principle that any valid pointer
value (that isn't null) points to some object.
I agree that the Standard doesn't say all this as clearly or as
directly as I would like. But I believe it is the most consistent
reading of what the Standard does say, and also is how WG14
intends and expects the Standard will be interpreted. I don't
have any specific examples to cite in support of this last part;
as often happens with deeply held assumptions, it comes out more
as a general sense from the writing (including the Standard, DR's,
and the Rationale document primarily) than from any specific
statement or set of statements.