There is a statement about this in the C standard; C 2024 6.3.1 says “Unless explicitly stated otherwise, conversion of an operand value to a compatible type causes no change to the value or the representation.” So if we have some value represented in one type, then it must have the same representation in a compatible type, unless explicitly stated otherwise. I am not aware of any explicit statements otherwise.
Otherwise it is not spelled out in the standard. However, compatible types are essentially types that could be the same if they or their subcomponents were suitably completed. For example, int [3] and int [] are compatible because int a[] could be completed to be int [3].
For an example of completing a type, b could be initially declared extern int b[];. It is some array of int defined elsewhere in the program, and we do not know how big it is. Later, there could be another declaration extern int b[7]; or even a definition int b[7] = {0, 1, 2, 3, 4, 5, 6};.
This answers your question both yes and no. The answer is no because int [] and int [3] are compatible because the int [] could be completed to be int [3], but it also could be completed instead to int [7]. So we know the int [3] has a representation of 3 int in memory, each with the representation of an int, but we do not know what the representation of int [] is, because it could could be 3 int in memory, but it could be 7 int, which is different, or any other positive number of int.
The answer is sometimes yes because the essence of compatibility is that the types could be the same once completed, and so their essential components at least must not only be compatible but must in fact be the same. So those essential components have the same representation. In other words, each of the elements of an int [] must have the same representation as the corresponding elements of an int [3], because they are the same type, int.
In particular, for types that can be accessed, this rule in the standard, from the aliasing rules in C 2024 6.5.1, compels compatible types to have the same representation:
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,…
This rule allows a program to store a value with one type and read it with a compatible type. Presumably we expect the read to produce the value that was stored, and so the representation must be the same for the two types. (An example of a type that cannot be accessed is an array. An array is not an lvalue, so an array object cannot be stored or read as an array.)
Note that we can have complete types that point to incomplete types. int (*p)[]; declares a pointer to an array of unknown size. This is a complete type and can be a definition; the compiler knows how much space to allocate for p even though it does not know how much space the array it points to requires. Compatibility of types includes this secondary incompleteness: int (*)[] is compatible with int (*)[3]. Further, C 2024 6.2.5 specifically says “Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements.”
Furthermore, is the sizeof guaranteed to be the same on compatible types?
Only if sizeof is defined. If we only have extern int b[];, then sizeof b is not defined. (It is a constraint violation.) sizeof is defined only for complete types, and, if two compatible types are complete, they must have the same size.
signed charandlong longcompatible types for small enough values.signed charandlong longare definitely not compatible. Not evensigned charandcharare compatible.