【问题标题】:How does dereference work C++取消引用如何工作 C++
【发布时间】:2015-09-23 07:31:37
【问题描述】:

我无法理解调用 &*pointer 时会发生什么

int j=8;
int* p = &j;

当我在编译器中打印时,我得到以下信息

j = 8 , &j =  00EBFEAC  p = 00EBFEAC , *p = 8 , &p =  00EBFEA0 
&*p= 00EBFEAC

cout << &*p gives  &*p = 00EBFEAC which is p itself

& 和 * 具有相同的运算符优先级。我认为 &*p 会转换为 &amp;(*p)--&gt; &amp;(8) 和预期的编译器错误。

编译器如何推导出这个结果?

【问题讨论】:

    标签: operator-keyword


    【解决方案1】:

    您遇到了一些有趣的事情:变量,严格来说,不是值,而是引用值。 8 是一个整数值。在int i=8 之后,i 一个整数值。不同之处在于它可以引用不同的值。

    为了获取该值,必须取消引用i,即必须获取存储在i代表的内存位置中的值。每当请求变量引用的类型的值时,此取消引用在 C 中隐式执行:i=8; printf("%d", i) 导致与printf("%d", 8) 相同的输出。这很有趣,因为变量本质上是地址的别名,而数字文字是立即值的别名。在 C 中,这些非常不同的东西在句法上是相同的。变量可以代表表达式中的文字,并将自动取消引用。生成的机器代码使这一点非常清楚。考虑下面的两个函数。两者都具有相同的返回类型 int。但是f 在 return 语句中有一个变量,必须取消引用才能返回它的值(在这种情况下,它是在寄存器中返回的):

    int i = 1;
    int g(){ return 1; }  // literal
    int f(){ return i; }  // variable
    

    如果我们忽略管理代码,则每个函数都会转换为一条机器指令。对应的汇编器(来自 icc)是为g

        movl      $1, %eax                                      #5.17
    

    这很简单:将 1 放入寄存器 eax。

    相比之下,f 转换为

        movl      i(%rip), %eax                                 #4.17
    

    这会将值放在寄存器 rip 中的地址加上寄存器 eax 中的偏移量 i。看到变量名只是编译器的地址(偏移)别名,令人耳目一新。

    必要的取消引用现在应该很明显了。写return *i 来返回 1 会更合乎逻辑,而写return i 只用于返回引用或指针的函数。

    在你的例子中,在某种程度上确实不合逻辑

    int j=8;
    int* p = &j;
    printf("%d\n", *p);
    

    打印 8(即 p 实际上被取消引用两次);但是 &amp;(*p) 产生 p 所指向的对象的地址(这是存储在 p 中的地址值),并且不会被解释为 &amp;(8)。原因是在地址运算符的上下文中,变量(或者,在这种情况下,通过取消引用 p 获得的 L 值)没有像在其他上下文中那样隐式取消引用。 p>

    当试图创建一种逻辑的、正交的语言——Algol68——时,int i=8 确实为 8 声明了一个别名。为了声明一个变量,长格式应该是 refint m = loc int := 3。因此,我们所说的指针或引用将具有 ref ref int 类型,因为实际上需要两次取消引用才能获得整数值。

    【讨论】:

    • 感谢您指出使用汇编代码。它让我更清楚
    【解决方案2】:

    j 是一个值为 8 的 int,存储在内存中的地址 00EBFEAC。

    &j 给出变量 j (00EBFEAC) 的内存地址。

    int* p = &j 在这里你定义了一个你定义为 int * 类型的变量 p,即内存中可以找到 int 的地址的值。你给它赋值 &j,也就是一个 int 的地址 -> 这是有道理的。

    *p 为您提供与存储在 p 中的地址关联的值。 p 中存储的地址指向一个 int,因此 *p 为您提供该 int 的值,即 8。

    &p是变量p本身存放的地址

    &*p 为您提供存储在 p 中的内存地址指向的值的地址,这确实是 p 。 &(*p) -> &(j) -> 00EBFEAC

    【讨论】:

      【解决方案3】:

      想想&amp;j 本身(甚至是&amp;(j))。根据您的逻辑,j 不应该评估为 8 并导致&amp;8,以及?取消引用指针或评估变量会产生 左值,这是一个您可以分配或获取地址的值。

      “左值”中的L指的是“赋值左侧”中的left,如j = 10*p = 12。还有一些右值,比如j + 10,或者8,显然不能赋值。

      这只是一个基本的解释。 In C++ there's a lot more to it,具有各种类型的值(但对于您当前的需求,该线程可能太先进了)。

      【讨论】:

      • 取消引用一个指针--> 你得到左值,你可以在它上面获取操作符的地址。我同意。在执行 &*p 的复杂步骤中,*p 的中间左值是多少?
      • “中间左值”是什么意思? *p 是一个左值,因为 p 指向 j,所以它和j 是同一个左值。
      • 我得到了答案,谢谢!我的印象是 *p in (&*p) 评估为 &(8) ,所以很困惑。现在我很清楚 *p in &*p 的计算结果是 &(*p)--> &(j) --> p
      • @Sree 太好了,如果它解决了您的问题,那么您可以接受答案:) 是的,您可以从概念上考虑这种方式,但实际上并没有这样的“重写”。左侧的*p 真的相当于j
      猜你喜欢
      • 2014-07-23
      • 1970-01-01
      • 2018-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-21
      相关资源
      最近更新 更多