Is always the address of a reference equal to the address of origin?

醉酒当歌 提交于 2019-12-06 11:09:20

问题


This looks a very basic topic but very important to me.

The following example shows that the address of a reference variable is equal to the address of the original variable. I know this is what we can expect from the concept of C/C++. However, is it always guaranteed that these addresses are equal under any circumstance?

#include <iostream>
#include <vector>
#include <string>

class Point
{
public:
    double x,y;
};

void func(Point &p)
{
    std::cout<<"&p="<<&p<<std::endl;
}

int main()
{
    std::vector<Point> plist;
    plist.push_back({1.0,4.0});
    plist.push_back({10.0,20.0});
    plist.push_back({3.0,5.0});
    plist.push_back({7.0,0.4});

    std::cout<<"&plist[2]="<<&plist[2]<<std::endl;

    func(plist[2]);

    return 0;
}

Result:

&plist[2]=0x119bc90
&p=0x119bc90

回答1:


Unfortunately, many people confuse logical and physical meaning of C++ references.

Logical level
There is a crucial thing I found for myself about C++ references:
As soon as I have initialized a reference, it becomes unpronounceable. And what I mean by "unpronounceable" is the fact that always when you name a reference in runtime executable code - you automatically get the variable it refers to, so there is no way you can touch the reference itself. Reference is just an alternative name (an alias) for a variable.

So if you have something like int i_var = 10; int& i_ref = i; no matter what executable expression you form with it, any mentioning of i_ref would actually mean i_var.

Also, some people find helpful to think of a reference as a "self-dereferencing pointer". So imagine you have a pointer int* p, but each and every time you refer to it as p you actually mean *p. For instance, p = 10 would mean *p = 10 and &p would mean &(*p) - the address of int which p is pointing too. That is how logically references work.

It applies to your code too. As soon as you have Point &p = plist[2]; (occured when you called func(plist[2])) then p and plist[2] start reference to the same thing - some Point object stored by the index of 2 in plist. So now &plist[2] and &p are absolutely equal.

Type system level
If you noticed, I used terms "runtime executable code" or "executable expression". Let me clarify.
The compiler actually knows the difference between a and b:

int a = 0;
int& b = a;

std::cout << std::boolalpha 
          << std::is_same_v<decltype(a), decltype(b)>; // -> false

As you see a and b types are different. However, std::is_same_v<decltype(a), decltype(b)> gets evaluated at compile time so i did not consider it as an "executable expression".

Physical level
Notice, that until now I did not said that the address of a reference and the address of variable being referenced are the same. Why? Because if you think logically - they are not.

References have to be implemented in some way, whether you like it or not. I believe, in the example with i_var and i_ref compiler will simply replace all i_ref with i_var and any physical representation of "reference" will never exist. On the other hand, if you store reference inside a class it is likely to be implemented with a pointer.
Although, an implementation is compiler dependent, if reference actually is a pointer under the hood, it is obvious that the address of this pointer and the address of the object it is pointing to are different.

However, why should you care? You will never know the address of reference! In any executable expression, when you say i_ref you imply i_var, remember?:)


OK, if you are really-really curious "what is the address of a reference", there is a one case when you can figure it out - when reference is a member of a class:

int main()
{
    int var = 10;
    int& real_ref = var;
    struct { int& ref; } fake_ref = { var };

    std::cout << &var       << std::endl;   // address of var
    std::cout << &real_ref  << std::endl;   // still address of var
    std::cout << &fake_ref  << std::endl;   // address of reference to var

    std::cout << sizeof var         << std::endl;    // size of var
    std::cout << sizeof real_ref    << std::endl;    // still size of var
    std::cout << sizeof fake_ref    << std::endl;    // size of reference to var

    return 0;
}

Output on x64 compiler:

000000A9272FFBA4   <- same
000000A9272FFBA4   <- same
000000A9272FFBC0   <- different
4                  <- same
4                  <- same
8                  <- different (8 on 64 bit and 4 on 32 bit compiler)



回答2:


Is always the address of a reference equal to the address of origin?

Objects have addresses. A reference is not an object and does not (necessarily) have an address. When address-of operator is applied to a reference, the result is the address of the referenced object.

is it always guaranteed that these addresses are equal under any circumstance?

Yes, it is guaranteed (unless the address-of operator is overloaded with a silly implementation).

An object that is referred by a reference is the same object as the one that is referred by that reference. An object has the same address as that object has... because it is that object :)


Now, it is possible to overload the addressof operator such that it no longer returns the actual address of that object. In that case, those two invocations could result in a different address. A demo:

struct evil {
    static evil silly;
    static bool insane;
    evil* operator&() {
        evil* bonkers = insane ? std::addressof(silly) : this;
        insane = !insane; // does this mean it is no longer insane?
        return bonkers;
    }
};

bool evil::insane = true;
foo evil::silly;

int main() {
    evil e;
    evil& ref = e;
    std::cout << &e << '\n';
    std::cout << &ref << '\n';
    std::cout << &ref << '\n';
}

Possible output:

0x7ffffbeef42d
0x600dd1
0x7ffffbeef42d



回答3:


is it always guaranteed that these addresses are equal under any circumstance?

yes, you are passing the reference of that Object so no copy is made, (if that is the background of the question), what you are printing is exactly the address of the original parameter by the time you call the function.




回答4:


Is it always guaranteed that these addresses are equal under any circumstance?

As per the C++ standard it is unspecified whether or not a reference requires storage. On the other hand it also says that taking the address of a reference gives you the address of the referent. Hence even if the compiler chooses to have a separate storage for the reference than the referent internally, it would be guaranteed to the programmer that their addresses are the same.



来源:https://stackoverflow.com/questions/45821678/is-always-the-address-of-a-reference-equal-to-the-address-of-origin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!