Cast pointer type in C - c

#include <stdio.h>
int main () {
char c = 'A';
int *int_ptr;
double *double_ptr;
*int_ptr = *(int *)&c;
*double_ptr = *(double *)&c;
printf("Original char = %c \n", c);
printf("Integer pointer = %d \n", *int_ptr);
printf("Double pointer = %f\n", *double_ptr);
return 0;
}
The questing is – Why can't I assign the double_ptr using this code, because it causes segmentation fault, but works fine for integer?
As I understand char is 1-byte long and int is 4-bytes long, so double is 8 bytes-long.
By using expression *(double *)&c I expect the following:
& – Get the memory address of c.
(double *) – pretend that this is a pointer to double.
*() – get the actual value and assign it to double var.

Your code has Undefined Behaviour. Therefore anything could happen.
The UB is because you are casting a char which is one byte to types that are 4 and 8 bytes, which means you are (potentially) accessing memory out of bounds, or with the wrong alignment.
Whether any of this will "work" or "not work" on any particular system is not very relevant, because the code is erroneous.

In your program, typecast of char to int* or double* and then a dereference would get some number of extra bytes from memory, which is undefined behavior.

Related

How to access data of another variable by pointing to a slack-byte in a structure?

#include <stdio.h>
#include <stdlib.h>
struct someStruct {
int ivar;
char cvar;
float fvar;
};
main(argc,argv)
const char** argv;
{
struct someStruct someObj;
someObj.ivar = 1;
someObj.fvar = 2.3;
someObj.cvar = 'r';
printf("%u\n",&someObj.fvar);
printf("%u\n",(&someObj.cvar + 4));
printf("%f\n",*((&someObj.cvar) + 4));
printf("%f\n",someObj.fvar);
}
This is a program I wrote which tries to access the address of fvar by adding 4 to the address of char.
I know the concept of slack-byte. I tried to access that memory, and lo! it printed the address correctly.
printf("%u\n",&someObj.fvar);
printf("%u\n",(&someObj.cvar + 4));
Both print the same address.
Then I tried to access fvar in the following way:
printf("%f\n",*((&someObj.cvar) + 4));
printf("%f\n",someObj.fvar);
and
0.000000
2.300000
were my results.
Then I realised that char* and float* are interpreted differently.
So, I tried all kinds of typecasting from char* to float* to float and also from char to float* to float and that too at all possible points like for e.g., typecasting the returned address to float,using 4.0 instead of 4 (which I realised wasn't supposed to work... and it didn't),etc.
It somehow just doesn't print 2.300000 and keeps printing 0.000000
Where am I missing the concept?
Note that I have a 64-bit MinGW and adds slack-byte (I know some don't) which I've already verified by:
printf("%u",sizeof(someObj.ivar));
printf("%u",sizeof(someObj.fvar));
printf("%u",sizeof(someObj.cvar));
printf("%u",sizeof(someObj));
which yields 4, 4, 1 and 12 ....(sizeof(char) + 3) respectively.
P.S. I know this is a horrible idea, but this is how I usually learn concepts XD
Assuming that the following is true:
&someObj.cvar + 4 == &someObj.fvar
you can cast the pointer value to a proper type:
printf("%f\n", *(float*)((&someObj.cvar) + 4));
(&someObj.cvar) is a pointer to char, so *(&someObj.cvar) is a char. The %f printf specifier expects a double or a float, passing a char is invalid. Note: float when passed in variadic functions as one of the arguments in ellipsis parameter is implicitly converted to double, see ex. cppreference implicit conversions. You have to pass a double or a float to %f, not a char.
Notes:
main(argc,argv)
const char** argv;
{
Don't use implicit int declaration and old, deprecated, obsolete style of function declaration. Use the normal style:
int main(int argc, char **argv) {
printf("%u\n",&someObj.fvar); is undefined behavior. The %u expects unsigned char, not float *. You can print a void* pointer using %p specifier: printf("%p\n", (void*)&someObj.fvar); or cast it to unsinged int: printf("%u\n", (unsigned int)(uintptr_t)(void*)&someObj.fvar);
C has an macro offsetof declared in stddef.h to access the number of "slack-bytes" (I like the name "padding bytes" better). You can: printf("%f", *(float*)((char*)&someObj + offsetof(struct someStruct, fvar)));

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;
}

Variable initialization and pointer segfault

I was thinking about pointer initialization and tried the following two cases:
#include <stdio.h>
int main(int argc, char **argv)
{
int *d;
int f;
int p;
*d = 6;
printf("%d %d %d\n", *d, f, p);
return 0;
}
This code segfaults at the line (seen in gdb):
*d = 6;
Which makes sense because I am trying store a value at a random address.
I then tried the following:
#include <stdio.h>
int main(int argc, char **argv)
{
int *d;
int f = 10;
int p = 9;
*d = 6;
printf("%d %d %d\n", *d, f, p);
return 0;
}
This runs to completion with the output:
6 10 9
If only f is initialized to 10 (p is not initialized) or vice versa, the program still completes with output
6 10 0
or
6 0 9
(p initialized). I do not understand why this works. My initial guess is that the initialization of either f or p makes space or orients memory to allow for the safe initialization of d. I also thought about stack allocations, but am still unsure.
Your first problem is in *d = 6; as you're trying to to dereference an invalid pointer.
d is not initialized (allocated memory) and it points to an invalid memory location. Any attempt to dereference that will lead to undefined behavior.
FWIW, the second code also produces UB for the same reason.
Additionally, in the first code snippet, by writing
printf("%d %d %d\n", *d, f, p);
where f and p are uninitialized automatic local variable, you're trying to read indeterminate values, which again, produces UB. This is avoided in the second snippet by explicit initialization.
In your program -
*d = 6; // writing to an invalid memory location
You leave the pointer with uninitialized value. So when you dereference it (*d), you access unauthorized location in memory, resulting in a segmentation fault.
you also try to print uninitialized local variables-
printf("%d %d %d\n", *d, f, p); // where f and p are indeterminate
Therefore , your code invokes undefined behaviour and your program give output which literally can be anything.
This code segfaults at the line (seen in gdb):
*d = 6;
Which makes sense because I am trying to give random address to a
value.
It does make sense that that segfaults, but your explanation is grossly incorrect. You are not in any way giving / or assigning an address to a value. Rather, you are attempting to store a value at an unspecified address.
It is critically important to understand the difference between d and *d. The former (as you have declared it) is a pointer that is expected to hold the address of an int. The latter is the int that d points to. If d has not, in fact, been initialized to point to an int, then evaluating the expression *d produces undefined behavior.
Undefined behavior is exactly that -- undefined. You cannot expect a similar source of undefined behavior to produce the same actual behavior in other circumstances, and you cannot even rely on the behavior to manifest an obvious indication of brokenness. Anything can happen, including, in principle, what the programmer wanted to happen.
Declaring a pointer variable does not automatically cause any storage to be allocated. There are any number of ways you could initialize d, but one would be
d = &f;
You need to Allocate your memory with malloc()
this work:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int *d;
int f = 10;
int p = 9;
d = /*(int *)*/malloc(sizeof(int) * 1);
if (!d)
exit(-1);
*d = 6;
printf("%d %d %d\n", *d, f, p);
free(d);
return 0;
}

pointer arithmetic: address with no assignment holds value of another address

why is the address px+1 holding the value 0.3? (on every execution) the variable f has also the value 0.3, but it has an other address(px-1)!? (even the values on px+2, px+3 and px-2 hold the value 0.3..., when I print it out)
#include <stdio.h>
int main(){
int* px;
int i = 1;
float f= 0.3;
double d=0.005;
char c = '*';
px=&i;
printf("Values: i=%i f=%f d=%f c=%c\n",i,f,d,c);
printf("Addresses: i=%lX f=%lX d=%lX c=%lX\n",&i,&f,&d,&c);
printf("Pointer Values (size of int = %d):\n px=%lX; px+1=%lX; px+2=%lX; px+3=%lX\n",sizeof(int),px,px+1,px+2,px+3);
printf("Dereference: at px+1=%lX, value is:%f \n",px+1,*(px+1));
/* Output :
Values: i=1 f=0.300000 d=0.005000 c=*
Addresses: i=7FFF5C546BB4 f=7FFF5C546BB0 d=7FFF5C546BA8 c=7FFF5C546BA7
Pointer Values (size of int = 4):
px=7FFF5C546BB4; px+1=7FFF5C546BB8; px+2=7FFF5C546BBC; px+3=7FFF5C546BC0
Dereference: at px+1=7FFF5C546BB8, value is:0.300000
*/
}
You have several problems, all leading to undefined behavior. First of all, you should use "%p" to print pointers, and the pointers should be casted to void * for maximum compatibility. Secondly, dereferencing a pointer out of bounds, like when you do *(px + 1) leads to undefined behavior. Thirdly, the pointer px points to an integer, but you try to print it using float conversion which also leads to undefined behavior.
By writing *(px+1), you're trying to access out of bound memory. It invokes undefined behaviour.
Also,
to print an address, you should be using the %p format specifier.
to print an int you should use %d or %i

Casting char pointer to int pointer - buffer error 10

In this answer, the author discussed how it was possible to cast pointers in C. I wanted to try this out and constructed this code:
#include <stdio.h>
int main(void) {
char *c;
*c = 10;
int i = *(int*)(c);
printf("%d", i);
return 1;
}
This compiles (with a warning) and when I execute the binary it just outputs bus error: 10. I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here. In addition, I'd like to know if there is a correct way to cast the pointers and dereference the int pointer to get 10 (in this example). Thanks!
EDIT: To clarify my intent, if you are worried, I'm just trying to come up with a "working" example of pointer casting. This is just to show that this is allowed and might work in C.
c is uninitialized when you dereference it. That's undefined behaviour.
Likewise, even if c were initialized, your typecast of it to int * and then a dereference would get some number of extra bytes from memory, which is also undefined behaviour.
A working (safe) example that illustrates what you're trying:
int main(void)
{
int i = 10;
int *p = &i;
char c = *(char *)p;
printf("%d\n", c);
return 0;
}
This program will print 10 on a little-endian machine and 0 on a big-endian machine.
These lines of code are problematic. You are writing through a pointer that is uninitialized.
char *c;
*c = 10;
Change to something like this:
char * c = malloc (sizeof (char));
Then, the following line is invalid logic, and the compiler should at least warn you about this:
int i = *(int*)(c);
You are reading an int (probably 4 or 8 bytes) from a pointer that only has one byte of storage (sizeof (char)). You can't read an int worth of bytes from a char memory slot.
First of all your program has undefined behaviour because pointer c was not initialized.
As for the question then you may write simply
int i = *c;
printf("%d", i);
Integral types with rankes less than the rank of type int are promoted to type int in expressions.
I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here
Some architectures like SPARC and some MIPS requires strict alignment. Thus if you want to read or write for example a word, it has to be aligned on 4 bytes, e.g. its address is multiple of 4 or the CPU will raise an exception. Other architectures like x86 can handle unaligned access, but with performance cost.
Let's take your code, find all places where things go boom as well as the reason why, and do the minimum to fix them:
#include <stdio.h>
int main(void) {
char *c;
*c = 10;
The preceding line is Undefined Behavior (UB), because c does not point to at least one char-object. So, insert these two lines directly before:
char x;
c = &x;
Lets move on after that fix:
int i = *(int*)(c);
Now this line is bad too.
Let's make our life complicated by assuming you didn't mean the more reasonable implicit widening conversion; int i = c;:
If the implementation defines _Alignof(int) != 1, the cast invokes UB because x is potentially mis-aligned.
If the implementation defines sizeof(int) != 1, the dereferencing invokes UB, because we refer to memory which is not there.
Let's fix both possible issues by changing the lines defining x and assigning its address to c to this:
_Alignas(in) char x[sizeof(int)];
c = x;
Now, reading the dereferenced pointer causes UB, because we treat some memory as if it stored an object of type int, which is not true unless we copied one there from a valid int variable - treating both as buffers of characters - or we last stored an int there.
So, add a store before the read:
*(int*)c = 0;
Moving on...
printf("%d", i);
return 1;
}
To recap, the changed program:
#include <stdio.h>
int main(void) {
char *c;
_Alignas(in) char x[sizeof(int)];
c = x;
*c = 10;
*(int*)c = 0;
int i = *(int*)(c);
printf("%d", i);
return 1;
}
(Used the C11 standard for my fixes.)

Resources