【问题标题】:Meaning of references, address-of, dereference and pointer引用、地址、解引用和指针的含义
【发布时间】:2020-11-29 12:09:39
【问题描述】:

这是我理解 C 和 C++ 中的 *& 符号的方式。

在 C 中,* 有两个用途。首先它可以用来声明一个指针变量,就像int* pointerVariable

然而,它可以用作像 *pointerVariable 这样的解引用运算符,它返回保存在该地址的值,它了解如何根据我们声明的指针指向的数据类型来解释该地址的字节。在我们的例子中 int* 因此它读取保存在该地址的字节并返回整数。

我们在 C 中也有地址运算符,例如 &someVariable,它返回保存在 someVariable 名称下的字节地址。

但是在 C++(不是 C)中,我们也可以在引用声明中使用 &,就像 int& someReference 一样。这会将变量someReference 转换为引用,这意味着无论我们传递给该变量的任何值,它都会自动获取我们传递给它的值的地址并保存它。

我理解正确吗?

【问题讨论】:

  • 那是因为您在这里有多个问题。 Stackoverflow 不会在一篇文章中处理多个问题。
  • 是的,您的理解似乎是正确的。
  • @rsjaffe:多个相关问题一起提出是合理的,特别是在主题重叠且需要比较和对比的情况下。
  • 在我看来,你的参考心态适得其反。引用只是现有对象的名称。它不是 C++ 程序处理的抽象意义上的地址。当试图将引用视为指针的语法糖时,会发生很多混乱(和错误)。

标签: c++ c


【解决方案1】:

我理解正确吗?

是的,但最好根据您想要做什么来考虑指针和引用。

对于那些需要引用某个对象而不复制它的情况,引用非常有用。引用很简单:它们始终有效,并且在您使用对象时语法没有变化。

指针用于其余情况。指针允许您使用地址(指针算法),需要显式语法来引用它们背后的对象(*&-> 运算符),可以为空(NULLnullptr),可以修改等。

总之,引用更简单,也更容易推理。当引用没有剪切它时使用指针。

【讨论】:

    【解决方案2】:
    1. 定义指针的一般语法:
      data-type * pointer-name = &variable-name
      指针的数据类型必须与它所指向的变量的数据类型相同。
      void 类型指针可以处理所有数据类型。
    2. 定义引用变量的一般语法:
      data-type & reference-name = variable-name
      引用变量的数据类型必须与作为别名的变量的数据类型相同。

    让我们看一下它们中的每一个,为了解释的目的,我将使用一个简单的 C 和 C++ 交换程序。


    在 C 中通过引用传递交换两个变量

    #include <stdio.h>
    void swap(int *,int *); //Function prototype
    int main()
    {
        int a = 10;
        int b = 20;
        printf("Before Swap: a=%d, b=%d\n",a,b);
        swap(&a,&b); //Value of a,b are passed by reference
        printf("After Swap: a=%d, b=%d\n",a,b);
        return 0;
    }
    
    void swap(int *ptra,int *ptrb)
    {
        int temp = *ptra;
        *ptra = *ptrb;
        *ptrb = temp;
    }
    
    • 在上面的代码中,我们声明并初始化了变量ab 分别为 1020
    • 然后我们传递a的地址 和 bswap 函数,使用 addressof (&amp;) 运算符。该运算符给出变量的地址。
    • 这些传递的参数被分配给各自的形式参数,在这种情况下是int 指针ptraptrb
    • 要交换变量,我们首先需要临时存储其中一个变量的值。为此,我们将指针ptra 指向的值存储到变量temp。这是通过首先使用dereference (*) 运算符取消引用指针,然后将其分配给temp 来完成的。 dereference (*) 运算符用于访问存储在指针指向的内存位置中的值。
    • 一旦ptra指向的值被保存,我们可以给它分配一个新的值,在这种情况下,我们给它分配了变量b的值(再次借助dereference (*)运算符)。并且ptrb 被分配了保存在temp(a 的原始值)中的值。因此,通过更改这些变量的内存位置来交换 a 和 b 的值。

    注意:我们可以像这样一起使用dereference (*) 运算符和addressof (&amp;) 运算符,*&amp;a,它们相互抵消,结果只是a


    我们也可以在 C++ 中使用指针交换两个数字来编写类似的程序,但是该语言支持另一个类型变量,称为 引用变量。它为先前定义的变量提供别名(替代名称)。

    在 C++ 中通过引用调用交换两个变量

    #include <iostream>
    using namespace std;
    void swap(int &,int &); //Function prototype
    int main()
    {
        int a = 10;
        int b = 20;
        cout << "Before Swap: a= " << a << " b= " << b << endl;
        swap(a,b);
        cout << "After Swap: a= " << a << " b= " << b << endl;
        return 0;
    }
    
    void swap(int &refa,int &refb)
    {
        int temp = refa;
        refa = refb;
        refb = temp;
    }
    
    • 在上面的代码中,当我们将变量ab传递给函数swap时,变量ab得到了它们各自的引用变量refarefbswap 内。这就像给一个变量另一个别名一样。
    • 现在,我们可以使用引用变量直接交换变量,而无需 dereferencing (*) 运算符。
    • 其余逻辑保持不变。

    【讨论】:

      【解决方案3】:

      所以在我们讨论指针和引用之间的区别之前,我觉得我们需要先谈谈声明语法,部分是为了解释为什么指针和引用声明是这样写的,部分是因为许多 C++ 程序员的写法指针和引用声明歪曲了该语法(请放心,这需要一段时间)。


      在 C 和 C++ 中,声明由一系列 声明说明符 和一系列 声明符1 组成。在像

      这样的声明中
      static unsigned long int a[10], *p, f(void);
      

      声明说明符是static unsigned long int,声明符是a[10]*pf(void)

      Array-ness、pointer-ness、function-ness 和 C++ 中的 reference-ness 都被指定为 declarator 的一部分,而不是声明说明符。这意味着当你写类似的东西时

      int* p;
      

      它被解析

      int (*p);
      

      由于一元 * 运算符是唯一标记,编译器不需要空格来将其与 int 类型说明符或 p 标识符区分开来。你可以写成int *p;int* p;int * p;,甚至int*p;

      这也意味着在像这样的声明中

      int* p, q;
      

      只有 p 被声明为指针 - q 是常规 int

      这个想法是变量的声明与其在代码中的使用非常匹配(“声明模仿使用”)。如果您有一个名为 p 的指向 int 的指针,并且您想访问指向的值,您可以使用 * 运算符取消引用它:

      printf( "%d\n", *p );
      

      表达式*p的类型为int,所以p的声明写成

      int *p;
      

      这告诉我们变量p 具有“指向int 的指针”类型,因为p 和一元运算符* 的组合为我们提供了@987654351 类型的表达式 @。大多数 C 程序员将编写如上所示的指针声明,* 明显与 p 分组。

      现在,Bjarne 和随后的几代 C++ 程序员认为强调 p 的指针性比 *pint-ness 更重要,因此他们引入了

      int* p;
      

      约定。但是,对于任何除了简单指针(或指向指针的指针)的约定,这个约定都失效了。它不适用于指向数组的指针:

      int (*a)[N];
      

      或指向函数的指针

      int (*f)(void);
      

      或指向函数的指针数组

      int (*p[N])(void);
      

      等等。将指针数组声明为

      int* a[N];
      

      只是表示思维混乱。由于[]() 是后缀,因此您不能通过编写将数组或函数与声明说明符相关联

      int[N] a;
      int(void) f;
      

      就像你可以使用一元 * 运算符一样,但一元 * 运算符以与 []() 运算符完全相同的方式绑定到声明符。2

      C++ 引用打破了关于“声明模仿使用”的规则hard。在非声明语句中,表达式&amp;x always 产生一个指针类型。如果x 的类型为int,则&amp;x 的类型为int *。所以&amp; 在声明中的含义与在表达式中的含义完全不同。


      这就是语法,让我们来谈谈指针与引用。

      pointer 只是一个地址值(尽管带有额外的类型信息)。您可以对指针进行(一些)算术运算,可以将它们初始化为任意值(或NULL),可以将[] 下标运算符应用于它们,就好像它们是一个数组一样(实际上,数组下标操作是 定义指针操作)。指针在首次创建时不需要有效(即包含对象在该对象生命周期内的地址)。

      reference 是对象或函数的另一个名称,而不仅仅是该对象或函数的地址(这就是为什么在处理引用时不使用 * 运算符的原因)。您不能对引用进行指针运算,不能为引用分配任意值等。实例化时,引用必须引用有效的对象或函数。没有具体说明引用在内部是如何表示的。


      1. 这是 C 术语 - C++ 术语略有不同。
      2. 如果现在还不清楚,我认为T* p; 习惯用法是不好的做法,并且会对指针声明语法造成不小的混淆;但是,既然 C++ 社区决定这样做,那我就是这样编写 C++ 代码的。我不喜欢它,它让我发痒,但为此争论或使用格式不一致的代码是不值得的。

      【讨论】:

        【解决方案4】:

        简单回答:

        引用变量是传递给它们的数据的别名,另一个标签。

        int var = 0;
        int& refVar = var;
        

        实际上,var 和 refVar 是同一个对象。

        值得注意的是,对堆指针数据的引用不能释放(delete)数据,因为它是数据的别名;

        int* var = new int{0};
        int& refVar = *var;
        delete refVar // error
        

        并且对指针本身的引用可以释放(delete)数据,作为指针的别名。

        int* var = new int{0};
        int*& refVar = var;
        delete refVar // good
        

        【讨论】:

          猜你喜欢
          • 2016-12-19
          • 2013-11-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-23
          • 2020-02-11
          • 2014-05-06
          • 1970-01-01
          相关资源
          最近更新 更多