There are no two dimensional arrays in C.
Yes, there are.
... in languages like BASIC, Fortran or Matlab.
C has no first class two dimensional arrays, only arrays of arrays are sometimes known as such, and all the usual array rules apply to them.
An argument in favor of such conversion is:
1. we are allowed to convert an array of arrays to a pointer to an array and use that as we please
2. we are allowed to convert an array to a pointer to its individual element and use that as we please
I understand your objection to be that the scalar pointer obtained in step 2 is only permitted to refer to elements of the particular "inner" array obtained in step 1 and that the implementation is permitted to blindly assume that it is so.
The "well-defined memory layout" does not matter. What matters is that you are working with an `int[3]` array and language rules do not permit access beyond its boundary. Otherwise the behavior is undefined. The compiler is allowed to assume that undefined behavior never happens and translate (optimize) the code under that assumption. For example, the compiler is allowed to, say, inline the `test` call and, say, unroll the cycle to 3 iterations tops (since there "can't possibly be" more than 3).
I agree that my "solution" was a blatant cheat on the type system and the above is a potential concern.
So let's go to the original code posted by Bruce, where the full array is simply passed as a void pointer. Now, the compiler would need to somehow conclude that the void* converted to int* by the calle somehow is a pointer to one of the inner arrays, rather than the full array of arrays which has been passed by the caller (note that when the array of arrays decays to a pointer, the pointer can still be used by the callee to access all of the array, otherwise memcpy wouldn't work). And we could go further, using memcpy to exctract individual elements and only then converting them to int.
From 6.5 Expressions in C11:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type
Aliasing an array using an incompatible type (arrays of different sizes and element types are not compatible) does not fall in any of the above cases, so, going against a "shall" outside of a Constraints section it's automatically UB.
[/quote]
I'm not 100% sure what this rule means in the context of accessing array members. Naive reading seems to imply that no access to an individual array member through a pointer is legal, if we read "the object" as "the array" and consider such member access to be an array access too.
Similarly, in Appendix J, J.2 Undefined Behavior:
— An array subscript is out of range, even if an object is apparently accessible with the given subscript (as in the lvalue expression a[1][7] given the declaration int a[4][5]) (6.5.6).
We are not accessing anything out of declared bounds, but declaring no bounds on an alias to an object declared as a particular array type elsewhere.
If there is something I would be concerned about in practical terms, it would be alias analysis.
Sure does work. I tried it on arm64, amd64, and riscv64 machines.
The discussion is whether it is permitted to stop working tomorrow out of a sudden.