Reputation: 53
I have found the following code in my lecture book, it works as intended, there is no error, i am just trying to understand it. In the code, we create a char array and an int_pointer. We are able to traverse the char array with the int pointer by typecasting it to a char pointer, such that everytime it gets incremented we move exactly 1 byte forward, instead of 4.
Now my question is why we typecast 2 times. In line 13 (where the comment is) there is the "inner" typecast of (char *) and additionally the "outer" typecast (int *). I understand why the inner typecast is necessary, but why do we need the outer one? I have removed it and yet everything stays the same.
What amazes me even more is that by typecasting it back into an int pointer, the expected length of the referenced data becomes 4 again and yet, when it gets dereferenced it prints out char after char, which indicates that only 1 byte is actually read out, even though we use a pointer that is of type int, which usually reads 4 bytes from where the pointer points to. How can that be?
#include <stdio.h>
int main(){
int i;
char char_array[5] = {'a','b','c','d','e'};
int int_array[5] = {1,2,3,4,5};
char *char_pointer;
int *int_pointer;
char_pointer = (char *) int_array; // Typecast into the
int_pointer = (int *) char_array; // pointer's data type
for (int i=0; i < 5;i++){ // Iterate through the char array with the int_pointer
printf("[integer pointer] points to %p, which contains the char '%c'\n",
int_pointer, *int_pointer);
/* Line 13 */ int_pointer = (int *) ((char *) int_pointer + 1);
}
for (int i=0; i < 5;i++){ // Iterate through the int array with the char_pointer
printf("[char pointer] points to %p, which contains the integer '%d'\n",
char_pointer, *char_pointer);
char_pointer = (char *) ((int *) char_pointer + 1);
} }
OUTPUT
[integer pointer] points to 000000000061FE03, which contains the char 'a'
[integer pointer] points to 000000000061FE04, which contains the char 'b'
[integer pointer] points to 000000000061FE05, which contains the char 'c'
[integer pointer] points to 000000000061FE06, which contains the char 'd'
[integer pointer] points to 000000000061FE07, which contains the char 'e'
[char pointer] points to 000000000061FDE0, which contains the integer '1'
[char pointer] points to 000000000061FDE4, which contains the integer '2'
[char pointer] points to 000000000061FDE8, which contains the integer '3'
[char pointer] points to 000000000061FDEC, which contains the integer '4'
[char pointer] points to 000000000061FDF0, which contains the integer '5'
Upvotes: 1
Views: 557
Reputation: 2422
Your question is answered by @paxdiablo, with a very well conceptual explanation but I'll add a couple of lines to make it easier to visualize.
Breaking down the line int_pointer = (int *)((char *) int_pointer + 1);
:
// set the int_pointer to point to the starting address of the char_array
int_pointer = (int *) char_array;
// treat the int_pointer as if it's a character pointer
(char *) int_pointer
// add 1 to that pointer *** adds 1 byte only ***
(char *) int_pointer + 1
// then, switch back and treat that pointer as if it's an integer pointer again
int_pointer = (int *) ((char *) int_pointer + 1);
And, breaking down the line char_pointer = (char *)((int *) char_pointer + 1);
:
// set the char_pointer to point to the starting address of the int_array
char_pointer = (char *) int_array;
// treat the char_pointer as if it's an integer pointer
(int *) char_pointer
// add 1 to that pointer *** adds 4 bytes ***
(int *) char_pointer + 1
// then, switch back and treat that pointer as if it's a character pointer again
char_pointer = (char *)((int *) char_pointer + 1);
Then the magic of the printf
function comes into play... If a printf
function is to print the value of a dereferenced pointer, printf
treats that same address according to the corresponding format specifier:
printf("dereferencing same address as int %d and as char %c\n", *char_pointer, *char_pointer);
// printf takes the same memory address, namely the value of "char_pointer"
// first, treats the 4 bytes as if it's an integer
// then, treats the single byte as if it's a character
and same as above;
printf("dereferencing same address as int %d and as char %c\n", *int_pointer, *int_pointer);
// printf takes the same memory address, namely the value of "int_pointer"
// first, treats the 4 bytes as if it's an integer
// then, treats the single byte as if it's a character
Upvotes: 2
Reputation: 881253
You would be very unlikely to see code like that outside an environment where they're just trying to teach you how things may work under the covers :-)
But I'll answer one question at least:
the expected length of the referenced data becomes 4 again and yet, when it gets dereferenced it prints out char after char, which indicates that only 1 byte is actually read out ...
No, the expression *int_pointer
will read out four bytes (or as many bytes as is needed to make an int
). What you're seeing there is the undefined behaviour of mismatching a format specifier with a different data type to what's expected. The standard is quite explicit (ISO C11 in this case):
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
What's probably happening with your code in this case is that it's just using the least significant eight bits of the int
.
And, just as an aside, it doesn't actually make any difference what the original type of a pointer is when you cast it to another type and perform arithmetic on it (expect for the possibility of having non-aligned pointers which is fatal in some environments).
The arithmetic is performed on the cast type, not the original:
int_pointer = (int *) ((char *) int_pointer + 1);
char_pointer = (char *) ((int *) char_pointer + 1);
// \___________________/
// |
// This is what you're adding one to,
// the pointer as if it was another type.
The first of those advances the pointer by one memory location regardless of the fact it uses an int
pointer, because the additice expression is being given a char
pointer due to the cast.
Similarly, the second advances by sizeof(int)
, even though it's adjusting (eventually) a char
pointer.
Upvotes: 4