C programming - unexpected output - c

I am not able to understand how the following code is working:
#include<stdio.h>
int main() {
int i = 100;
int *a = &i;
float *f = (float *)a;
(*f)++;
printf("%d", *a); //getting some garbage value
}
'f' is pointing to the same memory location as that of 'a'. So, (*f)++ should in turn increment the value of i to 101. Where am I going wrong?

Floats and ints are stored with different binary representations. When you cast a float to an int or vice versa, the compiler takes care of this for you. But in your case, you're casting an int* to a float*, so you're modifying a float that has the wrong value, since the binary representation didn't get converted.

Related

Cast pointer type in 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.

floating point constant in C, bit representation

It is possible to access to the bit representation of a floating point constant in C;
For example i'd like to assign
uint64_t x = //bit representation of 5.74;
which is represented by
0x40b7ae14
Do you think it is possible?
One way of achieving this is to use a union:
union {
double fltValue;
uint64_t uintValue;
} conversion;
conversion.fltValue = 5.74;
printf("%#llx\n", conversion.uintValue);
Updated with %#x thanks to Aracthor for mentioning it. And %#llx thanks to EOF.
For a working example (with float instead of double) see:
https://ideone.com/p4rH5l
It is possible, but is not portable because you cannot be sure of how a floating point value is represented : C standard does not define it.
You could simply use casting of pointers :
float x = 5.74;
void *pt = &x
uint64_t *ip = pt;
uint64_t i = *ip;
This is formal undefined behaviour because you are casting a pointer to a different type, and you should not do it because you add endian problems to the floating point representation.
The correct way would be :
float x = 5.74;
void *pt = &x
unsigned char *ip = pt;
ip now point to a unsigned char[] of size sizeof(float) containing the binary representation of a float. And no undefined behaviour was invoked because casting a pointer to a void *or a char * is always allowed.
#include <stdint.h>
#include <stdio.h>
int main(void) {
double x = 5.74;
uint64_t y = *((uint64_t*)&x);
printf("0x%016llx", y); /* 0x4016f5c28f5c28f6 */
}
If you are asking for the binary representation of float, then take an integer pointer..assign the address of the float variable to the integer pointer and by derefenencing the value of int pointer, get its binary form..and in the middle if you somehow want to avoid the warning generated by unusual typecasting then you can always use void*

Operations on different data types

When i was dividing a int variable with a float variable, it was giving correct answer. I thought data types needed typecasting to be, for one data type cant operate with another kind of data type. If not, then how does the processor decide it can operate int and float, but not int and char.
In cases where you have not explicitly typecast the variables, implicit typecasting is performed. An example of how implicit typecasting works :
int variable = (int / float ) -> variable will have an integer value.
float variable = (int / float ) -> variable will have a float value.
An example using char and int :
int a=0;
char c='c';
a = c;
cout<<"Ascii value of"<<c<<"is "<<a;

Casting two pointers (float* to int*)

I'm an IT student and today I had a C programming exam.
One of questions is a bit confusing: Is it possible to cast float* to int* ?
I said no whats your opinion?
You can cast, but you won't magically get an integer version of the float.
What happens is that you have a float somewhere in memory and then have an integer point to the same location. But the two are represented differently (e.g., two's complement vs IEEE 754), and you will get a vastly different integer as a result.
For example:
#include <stdio.h>
int main() {
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(float) = %zu\n", sizeof(float));
float m = 456.78f;
int *p = (int*)&m;
printf("Float: %f\n", m);
printf("Integer: %d\n", *p);
return 0;
}
On my system, this outputs:
sizeof(int) = 4
sizeof(float) = 4
Float: 456.779999
Integer: 1139041239
I print their sizes as well, to emphasize that they could be different as well.
E.g., if the integer happened to be larger, you would then touch memory outside of the float.
While this is probably not what you want, there are uses for it. For example, if you want to see the bit-representation for a specific float, you could cast it to something like uint8_t and inspect it up to sizeof(float).
Why not just try it out?
$ cat foo.c
#include <stdio.h>
int main(){
float f = 0.15625;
float *p = &f;
printf("%f\n", *p);
printf("%i\n", *((int *)p));
}
$ gcc foo.c
$ ./a.out
0.156250
1042284544
The float is 32-bits and you can read about how its represented: http://en.wikipedia.org/wiki/Single-precision_floating-point_format
Casting the float pointer to an int pointer is just telling C that "the data I'm pointing to is an int".
The float 0.156250 is represented by the binary 0111110001000000000000000000000, which represents the integer 1042284544.
EDIT: #csl beat me to the answer :)

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