Reputation:
int PileInts[1024];
char *Pile = (char *)PileInts;
What do these two lines of code do? I am thinking that the char *Pile = (char *)PileInts;
line creates a character named *Pile
that gives the value of the address at PileInts
. Am I right? Could I get a deeper explanation?
Upvotes: 2
Views: 253
Reputation: 214770
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
Upvotes: 1
Reputation: 44339
The line
int PileInts[1024];
creates an array object that consists of 1024 integers. The object can be accessed using the variable name PileInts
The line
char *Pile = (char *)PileInts;
creates a char pointer object and makes it point to the first char of the array object. The char pointer object is accessed using the variable name Pile
.
The char pointer Pile
can be used for accessing the individual bytes of PileInts
. Example:
#include <stdio.h>
int main(void) {
int PileInts[1024];
char *Pile = (char *)PileInts;
PileInts[0] = 1;
PileInts[1] = 2;
// Print the bytes/chars of the first two ints of PileInts
for (unsigned i= 0; i < (2 * sizeof PileInts[0]); ++i)
{
printf("0x%02x\n", *Pile); // Print what Pile points to
++Pile; // Increment Pile so it points to the next byte/char
}
return 0;
}
Possible output:
0x01
0x00
0x00
0x00
0x02
0x00
0x00
0x00
Note: The output may differ from system to system due to different endianess and/or integer size.
If you want to see the value of Pile
, i.e. the address it points to, you can change the code like:
#include <stdio.h>
int main(void) {
int PileInts[1024];
char *Pile = (char *)PileInts;
PileInts[0] = 1;
PileInts[1] = 2;
for (unsigned i= 0; i < (2 * sizeof PileInts[0]); ++i)
{
printf("Pile points to addresss %p where the value 0x%02x is stored\n",
(void*)Pile, *Pile);
++Pile;
}
return 0;
}
Possible output:
Pile points to addresss 0x7ffe5860b8e0 where the value 0x01 is stored
Pile points to addresss 0x7ffe5860b8e1 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e2 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e3 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e4 where the value 0x02 is stored
Pile points to addresss 0x7ffe5860b8e5 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e6 where the value 0x00 is stored
Pile points to addresss 0x7ffe5860b8e7 where the value 0x00 is stored
Upvotes: 3