Let's say I have an N*M flat array and I would like to use as a multidimensional array. But instead of manually indexing it as array[row*M + column] I thought I will point a pointer to a multidimensional array of it.
char flatArray[N*M];
char (*multidimensional)[M] = (char (*)[M])flatArray;
Then accessing the contents of flat array via the multidimensional pointer.
It works with and without optimization and compiles without warnings.
But my concern is that the compiler may want to be smart in the future and if the length of the row is not a power of 2 it may pad it so it can generate code that exploits the x86 addressing, so the array access would be a single mov eax, [4*ebx+ecx] instead of having to calculate the index first with a few instructions before each time the array is accessed.
The question is: Are standards compliant C compilers allowed pad the rows in a multidimensional array to generate faster code?
The Standard requires that for any type Foo, the size of a Foo[N] be exactly N * sizeof (Foo). It does not, however, specify any circumstances in which an object of type Foo[M][N] (or even a Foo[N]) may be accessed using an lvalue of type Foo, unless Foo happens to be a character type. Obviously it would be absurd to suggest that arrays of non-character type should not be accessible using subscript expressions, but the way N1570 6.5p7 is written doesn't actually allow that.
When using character types, the Standard seems to intend that a pointer to an object of size N may be converted to character type and treated as though it were a char[N]; functions like memcpy and fwrite rely upon such abilities. Still, the Standard doesn't explicitly say such a thing.
If one assumes that the intention of the Standard was to allow the elements of a Foo[M][N] to be accessed using an lvalue of a non-character type Foo in at least some circumstances, defining what those circumstances are would likely answer the question of when one may use a "flattened" pointer to access a multi-dimensional array.
Personally, I think that an lvalue D which is derived from an lvalue of another type (e.g., given Foo myArray[10][12]; Foo *p = myArray[5];, I would regard myArray[5] as being an lvalue derived from myArray, myArray[5][0] as derived from myArray[0], and *p as derived from myArray[5][0]) should be usable to access storage within the object identified by the original, at least until either:
The same storage is accessed via some means not derived from D.
An lvalue is produced that isn't derived from D, that will be used to access that storage in conflicting fashion, or produce another lvalue that will be.
Code enters a loop or function wherein either of the above occurs.
Applying that principle, and treating a cast to an array-element type as "laundering" the exact type of the array, casting a Foo[M][N] to a Foo* would yield a pointer which could access all elements of the array as long as the array isn't accessed via any other means. A compiler that makes any bona fide effort to stay out of a programmer's way should have no trouble with such code. Unfortunately, the Standard doesn't specify any situations where compilers are required to allow derived lvalues to access parent objects, and some compiler writers interpret that as an invitation to ignore the possibility that lvalues might access storage associated with other lvalues from which they are derived.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With