Understand the output of a C code with pointers - c

I am trying to understand, what's going on in this program. The output is -121 3. How do we get this output?
#include <stdio.h>
int main(void) {
int a = 903;
char *p = (char *) &a;
printf("%d ",*p++);
printf("%d",*p);
return 0;
}
Runnable Code at ideone

Well what happens...
903 equals 0x387 in hex.
int a = 903;
You make a pointer to it, and cast it to a signed char pointer:
char *p = (char *) &a;
That now points to the 0x387's LSB, which reads 0x87, and when treated as signed char, you get -121. Then you advance to the MSB (by incrementing the pointer).
printf("%d ",*p++);
Now you read the MSB, which is 3.
printf("%d",*p);
However, it's not a very good idea to cast int* to char*.

Related

How casting of char pointer to int pointer works?

I am learning C. As I went through pointers there I noticed some strange behavior which I can't get it. When casting character pointer to integer pointer, integer pointer holds some weird value, no where reasonably related to char or char ascii code. But while printing casted variable with '%c', it prints correct char value.
Printing with '%d' gives some unknown numbers.
printf("%d", *pt); // prints as unknown integer value that too changes for every run
But while printing as '%c' then
printf("%c", *pt); // prints correct casted char value
Whole Program:
int main() {
char f = 'a';
int *pt = (int *)&f;
printf("%d\n", *pt);
printf("%c\n", *pt);
return 0;
}
Please explain how char to int pointer casting works and explain the output value.
Edit:
If I make the below changes to the program, then output will be as expected. Please explain this too.
#include <stdio.h>
int main() {
char f = 'a';
int *pt = (int *)&f;
printf("%d\n", *pt);
printf("%c\n", *pt);
int val = (int)f;
printf("%d\n", val);
printf("%c", val);
return 0;
}
Output:
97
a
97
a
Please explain this behavior too.
For what the C language specifies, this is just plain undefined behavior. You have a char sized region of memory from which you are reading an int; the result is undefined.
As for what is likely happening: The C runtime ends up dumping some random garbage on the stack before main is even executed. char f = 'a'; happens to rewrite one byte of the garbage to a known value, but the padding to align pt means the remaining bytes are never rewritten at all, and have "whatever the runtime left behind" in them. So when you read an int out, on a little endian system, the low byte equals the value of 'a', but the high bytes are whatever garbage happens to be left in the padding space.
As for why %c works, since the low byte is still the same, and %c only examines the low byte of the int provided, all the garbage is ignored, and things happen to work as expected. This only works on a little endian machine though; on a big endian machine, it would be the high byte initialized to 'a', but the low byte (garbage) would be printed by %c.
You have define f as a char. This allocates typically 1 byte of storage in most of the hardware. You take the address of f, cast it to (int *) and assign it to an int * variable, pt. Size of integer depends on the underlying hardware - it could be 2 or 4 or even more. When you assign address of f to pt, the address that gets assigned to pt depends on factors such as int size and the alignment requirements. That is why when you print *pt, you see a garbage value. Actually, the ASCII value of 'a' is contained in the garbage, the position of which depends on the int size, endianness of the hardware, etc. If you print *pt with %x, you will see 61 in the output (61 hex is 97 in decimal).
<#include <stdio.h>
int main()
{
//type casting in pointers
int a = 500; //value is assgned
int *p; //pointer p
p = &a; //stores the address in the pointer
printf("p=%d\n*p=%d", p, *p);
printf("\np+1=%d\n*(p+1)=%d", p + 1, *(p + 1));
char *p0;
p0 = (char *)p;
printf("\n\np0=%d\n*p0=%d", p0, *p0);
return 0;
}

Printing pointer's address in C

I'm reproducing printf from scrap and I need to store pointers address into a string then print it, so first I cast void* into an unsigned int then itoa it to hexadecimal but the last three char are wrong.
int main(void)
{
char str[] = "printf from scrap!";
my_printf("MY_PRINTF:'%p'", (void*)str);
printf("\n PRINTF:'%p'\n\n", (void*)str);
return (0);
}
int conv_p(va_list args)
{
void *ptr;
unsigned int ptrint;
ptr = va_arg(args, void*);
ptrint = (unsigned int)&ptr;
my_putstr("0x7fff");
my_putstr(my_itoa_base_uint(ptrint, 16));
return (1);
}
Output:
MY_PRINTF:'0x7fff505247b0'
PRINTF:'0x7fff50524a20'
As you can see the last three char are wrong, is there any documentation about that?
In the second case, you're converting the address of the variable ptr to an int, rather than its value (the pointer you're interested in).
Replacing (unsigned int)&ptr; with (unsigned int)ptr; will give you consistent values.
And an additional aside: there's no guarantee unsigned int is large enough to represent the pointer value: you should use intptr_t or uintptr_t from <stdint.h>.

Why does second printf print 0

#include<stdio.h>
int main()
{
char arr[] = "somestring";
char *ptr1 = arr;
char *ptr2 = ptr1 + 3;
printf("ptr2 - ptr1 = %ld\n", ptr2 - ptr1);
printf("(int*)ptr2 - (int*) ptr1 = %ld", (int*)ptr2 - (int*)ptr1);
return 0;
}
I understand
ptr2 - ptr1
gives 3 but cannot figure out why second printf prints 0.
It's because when you substract two pointers, you get the distance between the pointer in number of elements, not in bytes.
(char*)ptr2-(char*)ptr1 // distance is 3*sizeof(char), ie 3
(int*)ptr2-(int*)ptr1 // distance is 0.75*sizeof(int), rounded to 0
EDIT: I was wrong by saying that the cast forces the pointer to be aligned
If you want to check the distance between addresses don't use (int *) or (void *), ptrdiff_t is a type able to represent the result of any valid pointer subtraction operation.
#include <stdio.h>
#include <stddef.h>
int main(void)
{
char arr[] = "somestring";
char *ptr1 = arr;
char *ptr2 = ptr1 + 3;
ptrdiff_t diff = ptr2 - ptr1;
printf ("ptr2 - ptr1 = %td\n", diff);
return 0;
}
EDIT: As pointed out by #chux, use "%td" character for ptrdiff_t.
Casting a char pointer with int* would make it aligned to the 4bytes (considering int is 4 bytes here). Though ptr1 and ptr2 are 3 bytes away, casting them to int*, results in the same address -- hence the result.
This is because sizeof(int) == 4
Each char takes 1 byte. Your array of chars looks like this in memory:
[s][o][m][e][s][t][r][i][n][g][0]
When you have an array of ints, each int occupies four bytes. storing '1' and '2' conceptually looks more like this:
[0][0][0][1][0][0][0][2]
Ints must therefore be aligned to 4-byte boundaries. Your compiler is aliasing the address to the lowest integer boundary. You'll note that if you use 4 instead of 3 this works as you expected.
The reason you have to perform a subtraction to get it to do it (just passing the casted pointers to printf doesn't do it) is because printf is not strictly typed, i.e. the %ld format does not contain the information that the parameter is an int pointer.

Can int pointer be casted to char *?

The below program tests for Little/Big endian on intel processor. Actually little endian is correct output. First I am casting int to char* and accessing its value without initialization to int *.I am not understanding second part of output. Here int pointer is casted to char *. So why is not int pointer not changed its alignment to char *?
00000000 00000000 00000011 01111111 = 895
0 0 3 127
int main() {
int num = 895;
if(*(char *)&num == 127)
{
printf("\nLittle-Endian\n");
}
else
{
printf("Big-Endian\n");
}
int *p = (char *)&num ;
if(*p == 127)
{
printf("\nLittle-Endian\n");
}
else
{
printf("Big-Endian\n");
}
printf("%d\n",*p);
}
o/p
Little-Endian
Big-Endian
895
The first half of your program using this comparison:
if(*(char *)&num == 127)
looks fine.
The second half of your program contains this assignment:
int *p = (char *)&num ;
Which isn't valid code. You can't convert pointer types without an explicit cast. In this case, your compiler might be letting you get away with it, but strictly speaking, it's incorrect. This line should read:
int *p = (int *)(char *)&num;
or simply this equivalent statement:
int *p = &num;
From this example, I'm sure you can see why your second test doesn't work the way you'd like it to - you're still operating on the whole int, not on the single byte you were interested in. If you made p a char *, it would work the way you expected:
char *p = (char *)&num;
Can int pointer be cast to char *?
Yes, it's only the inverse that would invoke undefined behavior, more precisely, using the result of a cast from char * to int * (since char is 1-byte aligned, so any data pointer type can safely be cast to char *).

pointers and memory addresses

I recently did an assignment using bit masking and shifting to manipulate a 4 byte int.
I got to wondering if it was possible to set a char pointer to the start of the int variable and then step through the int as if it was a 1 byte char by using the char pointer.
Is there a way to do this or something similar? I tried to set the char pointer to an int but when I step ahead by 1 it jumps 4 bytes instead.
Just trying to think of alternative ways of doing the same thing.
Of course you can, this code shows the behavior:
#include <stdio.h>
int main()
{
int value = 1234567;
char *pt = (char*) &value;
printf("first char: %p, second char: %p\n", pt, pt+1);
}
This outputs:
first char: 0x7fff5fbff448, second char: 0x7fff5fbff449
As you can see difference is just 1 byte as intended, this because arithmetic on pointers has been done after casting the type to a smaller kind of data.
I imagine this should do what you want:
int x = 42;
char *c = (char *) &x;
char byte0 = c[0];
char byte1 = c[1];
char byte2 = c[2];
char byte3 = c[3];
Yes a char pointer would step by 1byte at a time, you probably inadvertently cast it to an int.
Another complexity is the order of the bytes in an int, at least on Intel

Resources