-3

Let's pretend that some bad code put an int value to float address. In general, how I can retrieve that int value back?

skater_nex
  • 453
  • 1
  • 5
  • 11

2 Answers2

2

Note about this answer: The code below emulates the problem (of having stored an int in a memory location reserved for a float) using reinterpret_cast, and then solves the problem using reinterpret_cast again. This breaks the strict-aliasing rule (3.10/10 C++ Standard), which states that accessing an object through a (g)lvalue of a type that doesn't match the real type of the object (in a certain sense of match), invokes undefined behaviour.

For this reason the code given below shouldn't be used, and the solution, i.e. using reinterpret_cast, is at least dangerous (although it may often work). Please refer to Steve Jessop's answer for the correct solution. Thanks to him for pointing out my mistake (see the comments).


Original answer:
If you have the address of where the value is stored, and if the address is correctly aligned for integer access, you should be able to use reinterpret_cast for this:

int main()
{
  /* An integer and an uninitialized float: */
  int original = 45;
  alignas(int) alignas(float) float f;

  float *p = &f;

  /* "Erroneously" putting an int into the address of the float: */
  *reinterpret_cast<int*>(p) = original;

  /* Retrieving the int (this is what you were looking for): */
  int retrieved = *reinterpret_cast<int*>(p);

  std::cout << "Original: " << original
            << ", Stored float: " << f
            << ", Retrieved: " << retrieved
            << std::endl;
  return 0;
}

(The keyword alignas used above is from C++11. Your compiler might not accept it. In any case, that part of the code is my way to emulate the problem, it's not an essential part of the solution.)

Community
  • 1
  • 1
jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • it's great! but is there some 'one-line' answer, something using only pointers? – skater_nex Jan 24 '13 at 06:57
  • @skater_nex Most of this code is for emulating the problem. The answer is the one line that has `this is what you were looking for` in the comment. – jogojapan Jan 24 '13 at 06:58
  • i got it. but i really looking for NOT using 'reinterpret_cast' – skater_nex Jan 24 '13 at 07:03
  • I doubt there is any way to do this without `reinterpret_cast`. The C-style cast suggested by borisbn is in fact equivalent to a `reinterpret_cast` in this case. – jogojapan Jan 24 '13 at 07:07
  • My compiler does not support alignas, so I cannot test it, but are you sure that the reinterpret_cast(p) does anything at all? I am sure that essentially pointer to a float is implemented the same way as a pointer to any other type, that is a 64bit number in my case – Martin Drozdik Jan 24 '13 at 07:08
  • 1
    @MartinDrozdik The `reinterpret_cast` does not do anything at run-time, but it ensures that the compiler creates code that interprets the bits found at the address as representing an integer, not a float. So it ensures that dereferencing the pointer (which is what the `*` to the left of `reinterpret_cast` does) works in the desired way. – jogojapan Jan 24 '13 at 07:10
  • then how about `i = *((int*)&f);` – skater_nex Jan 24 '13 at 07:11
  • 1
    @MartinDrozdik I guess it should work without the `alignas` on nearly all common platforms. You'd get a problem (I think) typically only on platforms where `float` is smaller than `int`. – jogojapan Jan 24 '13 at 07:11
  • @skater_nex That interprets the _address_ of `f` itself as integer, not the value stored there. – jogojapan Jan 24 '13 at 07:12
  • anyway. need decision without `reinterpret_cast` – skater_nex Jan 24 '13 at 07:18
  • 1
    @skater_nex Oh you've edited your comment.. yes. That's the solution proposed by borisbn. That will work, too. But it will do the exact same thing as a `reinterpret_cast`. Fully equivalent. – jogojapan Jan 24 '13 at 07:27
  • @jogojapan, great. just what i need. – skater_nex Jan 24 '13 at 07:29
  • 1
    This isn't guaranteed to work. A sufficiently aggressive optimizer, relying the strict aliasing rules, is permitted by the standard to assume that an `int*` and a `float*` can never refer to the same location in memory. Since this isn't true, the optimizer might then re-order (or eliminate) instructions so that the address isn't necessarily written to and read in the order you expect. This isn't hypothetical, gcc with -O2 can break this kind of code (although I haven't tested whether it breaks this specific example). – Steve Jessop Jan 24 '13 at 09:17
  • @SteveJessop Wow. I didn't think of that. But that's only a problem with the specific code above, where the writing and reading are in one piece of code, right? The problem description given by the OP assumes (on whatever basis) that the `int` has been written into that `float` location. – jogojapan Jan 24 '13 at 09:20
  • @SteveJessop The example works for me with GCC 4.8 and -O3. But your point is still valid. – jogojapan Jan 24 '13 at 09:21
  • @jogojapan: yes, if the read and the write are "far enough apart" that the optimizer can't see them both at once (in different TUs, for example) then in practice it would be unable to re-order them (barring link-time optimization). But what with inlining, just putting them in different functions wouldn't ensure that. – Steve Jessop Jan 24 '13 at 09:23
  • @jogojapan: yeah, I'm working a compromise between what the standard says, vs. an implementation detail that actually breaks code. In standardese it is UB to access an object through an lvalue with type other than one of a list of types specified in the standard that depend on the "effective type". It's irrelevant what actual pointers you use to form the lvalues. In your code the effective type of `f` is float, and both of the lines with reinterpret casts access it using an lvalue of type `int`, which is UB. But I think you've explained why the exact optimization I describe won't break it. – Steve Jessop Jan 24 '13 at 09:42
  • Now you've got me doubting myself, though. I think it's "current type" in C++ rather than "effective type", which is the C terminology. I don't have the standard to hand, it's possible that in C++ (unlike C) your first assignment does in fact change the current type of `f` to `int`. If so then the only UB in your code is `<< ", Stored float: " << f`, which accesses it as `float`. – Steve Jessop Jan 24 '13 at 09:44
  • @SteveJessop Yes, in any case my answer talks about code that effectively breaks the strict aliasing rule as if that were completely acceptable. I will remove it as soon as I can. – jogojapan Jan 24 '13 at 09:44
  • Actually, I'll leave the answer (as an example of what you should not do), but @skater_nex, please accept Steve's answer instead of mine. – jogojapan Jan 24 '13 at 09:58
  • ok. but i really need to be sure, that `i = *((int*)&f);` is valid too – skater_nex Jan 24 '13 at 10:02
  • @skater_nex This is equivalent to a `reinterpret_cast` and therefore breaks the strict-aliasing rule in the same way. You shouldn't use it. – jogojapan Jan 24 '13 at 10:04
  • don't know, maybe my question is asked in a wrong way. i'm newbie in c++ – skater_nex Jan 24 '13 at 10:04
  • @skater_nex No, the question is good (except that it assumes a hopefully unlikely situation). And as you see, it's easy to come up with the wrong answer. – jogojapan Jan 24 '13 at 10:05
  • @SteveJessop (in reply to _Now you've got me doubting myself.._) I have added a link to a question that quotes the Standard (03 and 11). The way I interpret the strict-aliasing rule, the very attempt to cast `float*&` to `int*&` and then access it, breaks the rule. – jogojapan Jan 24 '13 at 10:08
1

The answer with reinterpret_cast<int*> violates strict aliasing and has undefined behavior.

If you want to do it with defined behavior:

int read_int_from_float(const float &f) {
    int result;
    std::memcpy(&result, &f, sizeof(result));
    return result;
}

This is after checking that float and int have the same size. They don't need to have the same alignment.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699