1

I am unable to get my head around what type of value does the deference operator accept as its argument. By value, here I mean is it lvalue or rvalue?

Consider the following code snippet:

int i = 1, *p = &i;
cout << *p++; // Prints 1, so far so good.

Now since postfix ++ returns the unincremented value (unlike its prefix counterpart which returns incremented object) of p, from the code above it appears that unary * accepts an rvalue as its only operand. But,

cout << *i;  // error: invalid type argument of unary ‘*’ (have ‘int’)

Okay, maybe the value of i doesn't represent a valid memory address. Now to confirm that unary * can work on an rvalue that represents a valid memory location, I tried something like this:

unsigned long add = p;  // error: invalid conversion from ‘int*’ to ‘long unsigned int’ [-fpermissive]
cout << *add;           // error: invalid type argument of unary ‘*’ (have ‘int’)

This also brings me to my next question. Assuming that the size of unsigned long is 64 bits (which happens to be true for the system I am working on), why is it not possible to copy the value stored in p (which happens to be a 64-bit memory address) into add? Or in other words, is there a way to store a memory address in a variable of non-pointer type (I would be surprised if this isn't possible because a memory address is after all also a value)?

Thanks

stillanoob
  • 1,279
  • 2
  • 16
  • 29
  • 2
    `*` can only be used on a pointer. Your codes `*i` and `*add` fail because `i` and `add` are integers, not pointers; nothing to do with value categories – M.M Jun 15 '16 at 11:04
  • Stop thinking about the number of bits. `int` and `double` might be both 123bits long, but are different types. `int` and `unsigned int` might be 62bits each but are different types. Pointers and integers are different types, even if both might have the same number of bits. In the vast majority of cases you don't need to convert pointers to integers or vice versa; this is a horrible code smell. – chi Jun 15 '16 at 12:36

2 Answers2

3

The built-in dereference operator takes a prvalue, which has to be of a pointer type. If the operand is a glvalue, it will be subjected to lvalue-to-rvalue conversion prior to dereference.

If you want to convert between data pointer types and equivalent-sized integral types, you can, but you must say that that is what you want to do by using reinterpret_cast. This is to prevent errors, e.g. forgetting to dereference a pointer-to-int.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Where in standard is stated that built-in dereference operator takes prvalue? First paragraph in 5.3.1 says that it takes pointer to object or pointer to function. It doesn't specify value category. I was able to use it also on xvalue: http://ideone.com/GT2Qm9 . – PcAF Jun 15 '16 at 11:00
  • 1
    @PcAF see [conv.lval]/1. In a context where prvalue is required (such as the operand of `*`), a glvalue may be converted to prvalue – M.M Jun 15 '16 at 11:06
  • About xvalue, my bad, I thought that lvalue-to-rvalue conversion applies to lvalues not glvalues. But question still remains. Which document are you reading? In [n4594](http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/n4594.pdf) [conv.lval]/1 there isn't such sentence. – PcAF Jun 15 '16 at 14:26
  • @PcAF [expr]/9: "Whenever a glvalue expression appears as an operand of an operator that expects a prvalue for that operand, [...] standard conversions are applied to convert the expression to a prvalue." The Standard doesn't appear to say when a prvalue is expected; this may be a defect. – ecatmur Jun 15 '16 at 14:29
  • @PcAF see also http://stackoverflow.com/questions/5099769/regarding-lvalue-to-rvalue-conversion-when-is-it-required – ecatmur Jun 15 '16 at 14:31
  • You misunderstood me. I was asking where in standard is stated that *unary * operator* takes prvalue. – PcAF Jun 15 '16 at 14:40
  • @PcAF that's what the question I linked to discusses; it appears that the language implying that operators take prvalues except where otherwise specified was lost between C++03 and C++11. – ecatmur Jun 15 '16 at 14:53
0

You cannot convert a pointer type to a non-pointer type without an explicit cast (reinterpret_cast or C-style cast). Or the class should have implicit conversion operator. Assuming that int or long will fit a pointer type is wrong and risky assumption. If you take that risk, yes, you can store a pointer into a non-pointer type:

int main()
{    
    int a, b;
    a = 10;
    b = (int)&a;

    a = 40;
    cout << *(int*)b << endl;

    *(reinterpret_cast<int*>(b)) = 160;
    cout << a << endl;
}
Ajay
  • 18,086
  • 12
  • 59
  • 105