-3

Why isn't variable b assigned the value 10?

int *a;
int c = 5;
float b = 5.1;

a = (int*)&b;
*a = 10;

printf ("a = %p, &b = %p, b = %lf, *a = %d\n", a, &b, b, *a);

result: b = 0.00000, *a = 10

Natindo
  • 1
  • 1
  • 6
    Because you were lucky. Had the change been assigned (perfectly possible) you wouldn't have asked. You wouldn't learn about **Undefined Behaviour**. – pmg Sep 15 '21 at 09:55
  • you are pointing to a float variable using int pointer. You should have `float *a = &b` – light1 Sep 15 '21 at 09:58
  • BTW: try `printf("b = %a\n", b);` – pmg Sep 15 '21 at 09:59
  • 1
    please read about "Strict Aliasing Rule". Link: https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8 it's mainly about C++ but most stuff is relevant for C as well. Basically, the compiler is free to do everything if one accesses a variable through a pointer of invalid type. – tstanisl Sep 15 '21 at 10:03
  • You have marked in an incorrect answer as accepted. In the C 2018 standard, clause 6.3.2.3, paragraph 7, says “A pointer to an object type may be converted to a pointer to a different object type…” Therefore, the answer is incorrect to say that `a = (int*)&b` is an invalid pointer conversion. – Eric Postpischil Sep 16 '21 at 11:17

2 Answers2

1

While we may assign a plain int to a float and vice versa, and then get an implicit conversion, this does not apply to pointers.

a = (int*)&b is an invalid pointer conversion. You lie to the compiler and say that the thing stored at address &b is an int. Which is it not. This is undefined behavior - the two different types could have different sizes - but it's also a "strict aliasing violation" What is the strict aliasing rule?

Since your code contains undefined behavior, anything can happen. What is undefined behavior and how does it work? One of many possible behaviours is that the system tries to re-interpret the binary representation as a float number where the 0xA ends up so far down in the fraction part that it gets rounded to zero. The program may as well crash or display any random garbage.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • `a = (int*)&b` is not an invalid pointer conversion, provided `&b` is suitably aligned for `int`, which the address of a `float` generally is. The sizes of the types are irrelevant to the conversion, and there is no aliasing violation in this conversion or assignment. An aliasing violation occurs in `*a = 10`. – Eric Postpischil Sep 15 '21 at 11:28
  • @EricPostpischil The sizes are naturally highly relevant, because if you access an object of effective type 32 bit float though a 16 bit integer pointer, you only get the part of the float that happen to be in the 2 lowest addressable bytes, whatever that might be depending on endianess. If you do the opposite: access a 16 bit int through a 32 bit float, you access the memory location out of bounds. All of it is undefined behavior for multiple reasons. – Lundin Sep 15 '21 at 14:00
  • The sizes are not relevant for the pointer conversion, and there is nothing undefined in the pointer conversion if the alignment is suitable for `int`. – Eric Postpischil Sep 15 '21 at 14:01
0

In *a = 10;, the memory of b, which was declared to be a float, is accessed using *a, for which the type is int. This violates the rule in C 2018 6.5 7:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

— a type compatible with the effective type of the object, …

It goes on to list some other allowed cases, but changing a float object through an int type is not among them. Because this rule is violated, the C standard does not define the behavior of the program. (C 2018 4 2: “If a "shall" or "shall not" requirement that appears outside of a constraint or runtime-constraint is violated, the behavior is undefined.”)

Although the C standard does not define the behavior, two results are common in current compilers:

  • The compiler knows the value 5.1 (approximately, after conversion to float) was stored in b. So it ignores any change attempted through *a and uses 5.1 for b in the printf statement.
  • The compiler stores in the int value 10 in the memory used for b. Later, when it gets b, the bits for the int 10 are reinterpreted as a float. The float type encodes values differently than the int type, so the bits 000…0001010 represent a very small number in the float type. Since the conversion %lf produces only six digits after the decimal point, the small number is display as “0.000000”.
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312