line creates a character named *Pile
that gives the value of the address at PileInts
No. It creates a pointer to character char*
named Pile
, which points to the lowest byte in the first int
in an array of int
-
Could I get a deeper explanation?
The code gives a pointer which can be used to access individual bytes of the first int
in the array, and from there, individual bytes of the next adjacent int
, until the end of the array.
This is possible because when an array name is used in an expression, it "decays" into a pointer to the first element of that array. Making PileInts
, when used in an array, equivalent to type int*
.
Converting from int*
to char*
is valid but fishy C. Some things to be aware of:
First of all, uint8_t*
should be used whenever one seeks to access raw data values. The char
type is highly problematic since it has implementation-defined signedness - it should not be used for anything else but for characters and strings. Is char signed or unsigned by default?
This pointer will point at the lowest address of the first int
. Which bytes this corresponds to is CPU-specific, namely it depends on endianess of the CPU. Using bitwise shift operators instead of pointers would have remove this CPU dependency, so depending on what you want to do with this pointer, it may or may not be the right tool for the task.
As for the pointer conversion itself, it is fine as per C17 6.3.2.3/7:
A pointer to an object type may be converted to a pointer to a different object type. If the
resulting pointer is not correctly aligned for the referenced type, the behavior is
undefined. Otherwise, when converted back again, the result shall compare equal to the
original pointer.
Meaning that something like (int*) ((char*)PileInts + 1)
would be an undefined behavior bug, because the int*
will be misaligned.
The same paragraph in the standard continues:
When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
This is the rule allowing us to iterate over any data type by using a character pointer (or preferably the equivalent uint8_t*
). The same is not true for any other pointer type, we could for example not use a short*
to do the same, thinking we iterate over 16 bit words.
There's also another rule preventing us from using any other pointer type, namely the rule regulating the compiler's internal type system and how different pointers may be an alias of each other. Informally known as the strict aliasing rule. This rule too has an exception for character types only. Meaning we can't do this:
int PileInts[1024];
char *Pile = (char *)PileInts;
short* sptr = (short*)Pile; // very questionable cast but not a bug yet
printf("%h", *sptr); // bug, data is accessed as wrong type, strict aliasing violation