I’m confused about effective type in the case of pointers to arrays in C. Does accessing an individual member via a pointer to an array impart an effective type only on the memory for that member or across all the memory encompassed by the array? Is the Standard clear in this regard?
int ( *foo )[ 10 ] = malloc( sizeof( *foo ) );
**foo = 123; //Or ( *foo )[ 0 ] = 123
//Is the effective type of the memory for (*foo)[ 0 – 9 ] now also int?
//Does the whole region now have an effective type?
//Or can this memory be used for other purposes?
Here's a practical example:
int (*foo)[ 10 ];
double *bar;
//Figure out the size an int padded to comply with double alignment
size_t padded_int_size =
( ( ( sizeof( int ) + alignof( double ) - 1 ) / alignof( double ) ) * alignof( double ) );
//Allocate memory for one padded int and 1000 doubles,
//which will in any case be larger than an array of 10 ints
foo = malloc( padded_int_size + sizeof( double ) * 1000 );
//Set our double pointer to point just after the first int
bar = (double*)( (char*)foo + padded_int_size );
//Do things with ( *foo )[ 0 ] or **foo
//Do things with bar[ 0 - 999 ]
Does the above code invoke undefined behavior?
I searched online and found that most discussions about aggregate types and effective type concerned struct pointers, not pointers to arrays. Even then, there seems to be disagreement and confusion over whether setting a single struct member imparts an effective type only for that member or for the entire block of memory that the struct would encompass.
If you are asking whether the effective type for the whole region is (one)
int, then I think it's very hard to make an argument for that. I would be inclined to say no, but as Eric remarked in comments, the language specification is not clear here.If you are asking whether the effective type of
*foois nowint[10], then there is an easier argument for that. I would be inclined to say "yes". In the event that that position is accepted, there is an even stronger argument for the nineint-sized sub regions at the tail of*fooeach having effective typeint, but that wouldn't be an unassailable position.See above.
This is not an exclusive alternative. Any or all of the allocated object can be used for other purposes, regardless of the answers to the previous questions. The effective type of an object without a declared type can be reassigned by writing to that object.
The computation and assignment of values for
fooandbarare fine. The rest depends at least in part on what things are done. If you write all or part of the space viafooand then read that same space back viabarthen the behavior is undefined. Other combinations of actions may have less clear definedness.