2

I have opened an ofstream file to write to, and I want to write 4 bytes of an unsigned int. I want the int to be zero, so I tried doing this:

outFile.write((char*)&unsigned int(0), sizeof(unsigned int));  // Expression must be an lvalue

unsigned int a = 0;
outFile.write((char*)&a, sizeof(unsigned int));         // This works fine

My understanding is that the lifetime of something like that unsigned int is up to the end of that line, in other words destroyed by the next semicolon. Is the only way to write something like this by creating a local beforehand? Also, I noticed that fstreams take char pointers, I was wondering is there any need for caution in copying stuff, for example in binary mode I expect it to write bytes exactly as it is in memory, but for example if I have an unsigned int of 255 will it copy this unsigned int bit for bit as it is? Shouldn't this have been something like a void pointer when dealing with raw memory copying?

Zebrafish
  • 11,682
  • 3
  • 43
  • 119

3 Answers3

1

A local variable is destroyed when it goes out of scope. This is normally at the next closing brace.

outFile.write((char*)&unsigned int(0)

is trying to take the address of something that doesnt exist. You have to instantiate the variable first

{
  ....code...
  {
    unsigned int a = 0;
    outFile.write((char*)&a, sizeof(unsigned int));
  }
  <- legally a no longer exists
  unsigned int b = 0; // b *might* now occupy the same stack space that was used to store a.  then again it might not, it depends on the compiler.
}
<- b no longer exists.

You can use unorthodox ways to access a local variable that has gone out of scope, depending on how the compiler builds the stack frame, the space previously occupied by said local may or may not be used by something that is now live.

outFile.write((char*)&a, sizeof(unsigned int));

will attempt to write as many bits as an unsigned int uses, bit for bit.

but I would use

outFile.write((char*)&a, sizeof(a));

because hard coding the size may cause you headaches later. if you change the data type of a to lets say double, but you forget to update the sizeof to sizeof(double)

CascadeCoder
  • 163
  • 10
  • 1
    I thought it does exist though, taking the address of &unsigned int(0) would be OK because it doesn't go out of scope until after that line, I thought. I was just trying to figure out a way to avoid the local and write it in its spot, would be great. – Zebrafish Jan 28 '17 at 04:54
  • &unsigned int(0) is trying to take the address of a data type, not a variable of that type. &unsigned int(0) doesnt actually create anything in memory, which is why the compiler complains. – CascadeCoder Jan 28 '17 at 05:07
  • @TitoneMaurice it does exist but you are not allowed to take the address of it – M.M Jan 28 '17 at 08:13
  • 1
    @CascadeCoder `unsigned int(0)` is a value of type `unsigned int`. It's not a "data type". It might create something in memory – M.M Jan 28 '17 at 08:14
  • @mm then why does sizeof(unsigned int(0)) produce an compiler error. However, size of sizeof((unsigned int)0) sure... – CascadeCoder Jan 28 '17 at 08:22
1

Defining a variable (lvalue) is the only way to access the memory of an int unsigned int a = 0;

rvalues may never exist in memory

(3 + 4) could be optimized to 7 at compile time.

Here are some examples of the differences

Difference between rvalue and lvalue

You should be careful when exporting integers without encoding them

This question explains some of the pitfalls of writing the raw bytes of an int

Community
  • 1
  • 1
Mollenkamp
  • 72
  • 3
  • 1
    This answer misuses the terms "lvalue" and "rvalue" which are categories of expression. (The MSDN page you link to makes the same mistake). You actually mean "variable" and "temporary object" respectively, in the first two sentences.. Also, temporary objects may exist in memory. – M.M Jan 28 '17 at 08:10
  • @jamollenkamp `int n; int &&r = move(n);` the `move()` expression is an example of an rvalue that exists in memory – LWimsey Jan 29 '17 at 01:11
1

You cannot take the address of an rvalue unless it is cast to an lvalue. The compiler will then create a temporary variable and take its address.

template<typename T>
remove_reference_t<T>& rtolvalue_cast(remove_reference_t<T> &&t)
{
    return static_cast<remove_reference_t<T> &>(t);
}

Then you could write:

outFile.write((char*)& rtolvalue_cast<unsigned int>(0), sizeof(unsigned int));

Technically this cast is a distant relative to std::forward<T> which would also be able to do that, except that it has been disabled in the library with a static_assert and there are probably good reasons for that. This cast is more like a technical artefact than something realistic.

Instead, create a temporary variable and take its address.

LWimsey
  • 6,189
  • 2
  • 25
  • 53