• C++:指针


    在前面的章节中,变量被解释为计算机内存中的位置,可以通过它们的标识符(它们的名称)来访问。这样程序就不需要关心内存中数据的物理地址了;它只是在需要引用变量时使用标识符。

    对于 C++ 程序,计算机的内存就像一连串的内存单元,每个内存单元大小为一个字节,并且每个都有一个唯一的地址。这些单字节存储单元的排序方式允许大于一个字节的数据表示占据具有连续地址的存储单元。

    这样,每个单元都可以通过其唯一地址轻松定位在内存中。例如,具有地址的存储单元1776总是紧跟在具有地址的单元之后1775and 前面有1777, and 之后776正好是一千个单元格,之前正好是一千个单元格2776。

    声明变量时,需要为存储其值的内存分配内存中的特定位置(其内存地址)。通常,C++ 程序不会主动决定存储其变量的确切内存地址。幸运的是,该任务留给程序运行的环境——通常是一个在运行时决定特定内存位置的操作系统。但是,程序能够在运行时获取变量的地址以便访问相对于它的特定位置的数据单元可能很有用。

    运算符地址 (&)
    变量的地址可以通过在变量名前加上 & 号 ( &) 来获得,称为地址运算符。例如:

    foo = &myvar;
    

    这会将变量的地址分配myvar给foo; 通过在变量名称前myvar加上地址运算符( &),我们不再将变量本身的内容分配给foo,而是分配它的地址。

    一个变量在内存中的实际地址在运行前是无法知道的,但是我们假设,为了帮助澄清一些概念,它myvar是在运行时放在内存地址中的1776。

    在这种情况下,请考虑以下代码片段:

    myvar = 25;
    foo = &myvar;
    bar = myvar;
    

    执行此操作后每个变量中包含的值如下图所示:
    在这里插入图片描述

    首先,我们将值分配25给myvar(我们假设其在内存中的地址为的变量1776)。

    第二条语句分配foo的地址myvar,我们假设它是1776。

    最后,第三条语句将包含在myvarto中的值赋值给bar。这是一个标准的赋值操作,在前面的章节中已经做过很多次了。

    第二条和第三条语句的主要区别在于地址运算符( &) 的外观。

    存储另一个变量地址的变量(如foo在前面的例子中)在 C++ 中被称为指针。指针是语言的一个非常强大的特性,在低级编程中有很多用途。稍后,我们将看到如何声明和使用指针。

    取消引用运算符 (*)
    如前所述,存储另一个变量地址的变量称为指针。据说指针“指向”它们存储地址的变量。

    指针的一个有趣特性是它们可以用来访问它们直接指向的变量。这是通过在指针名称前加上取消引用运算符( *) 来完成的。运算符本身可以理解为“指向的值”。

    因此,使用上一个示例的值,以下语句:

    baz = *foo;
    

    这可以读作:“baz等于”指向的值foo,并且该语句实际上会将值分配25给baz,因为foois ,并且(按照上面的示例)1776指向的值将是。 重要的是要明确区分指的是 value ,而(在标识符前面有一个星号)指的是存储在 address 的值,在这种情况下是。请注意包含或不包含取消引用运算符的区别(我添加了一个解释性注释,说明如何阅读这两个表达式中的每一个):177625
    在这里插入图片描述

    foo1776foo177625

    baz = foo;   // baz equal to foo (1776)
    baz = *foo;  // baz equal to value pointed to by foo (25)  
    

    因此,引用和取消引用运算符是互补的:
    &是地址操作符,可以简单地读为“地址”
    *是解引用操作符,可以读作“指向的值”

    因此,它们具有某种相反的含义:用 获得的地址&可以用 取消引用*。

    之前,我们执行了以下两个赋值操作:

    myvar = 25;
    foo = &myvar;
    

    在这两个语句之后,以下所有表达式的结果都会为真:

    myvar == 25
    &myvar == 1776
    foo == 1776
    *foo ==
    

    myvar第一个表达式很清楚,考虑到对 is 执行的 赋值操作myvar=25。第二个使用地址运算符 ( &),它返回 的地址myvar,我们假设它的值为1776。foo第三个有点明显,因为第二个表达式为真,并且对 is 执行的赋值操作foo=&myvar。第四个表达式使用了解引用运算符( *),可以理解为“指向的值”,而指向的值foo确实是25。

    所以,毕竟,你也可以推断,只要 指向的地址foo保持不变,下面的表达式也成立:

    *foo ==
    

    声明指针
    由于指针能够直接引用它所指向的值,因此指针指向 achar时与指向aint或 a时具有不同的属性float。一旦取消引用,就需要知道类型。为此,指针的声明需要包含指针将指向的数据类型。

    指针的声明遵循以下语法:指针指向的数据类型

    type * name;

    在哪里。type这种类型不是指针本身的类型,而是指针指向的数据的类型。例如:

    int * number;
    char * character;
    double * decimals;
    

    这是指针的三个声明。每个都旨在指向不同的数据类型,但实际上,它们都是指针,并且它们都可能会占用相同的内存空间(指针在内存中的大小取决于平台程序运行的地方)。然而,它们指向的数据不占用相同数量的空间,也不是相同类型:第一个指向inta ,第二个指向 a char,最后一个指向 a double。因此,虽然这三个示例变量都是指针,但它们实际上具有不同的类型:int*、char和double,具体取决于它们所指向的类型。

    请注意,星号 (*) 在声明指针时仅表示它是一个指针(它是其类型复合说明符的一部分),不应与前面看到的取消引用运算符混淆,但它也用星号 ( *) 编写。它们只是用相同符号表示的两种不同的事物。

    让我们看一个关于指针的例子:

    // my first pointer
    #include 
    using namespace std;
    
    int main ()
    {
      int firstvalue, secondvalue;
      int * mypointer;
    
      mypointer = &firstvalue;
      *mypointer = 10;
      mypointer = &secondvalue;
      *mypointer = 20;
      cout << "firstvalue is " << firstvalue << '\n';
      cout << "secondvalue is " << secondvalue << '\n';
      return 0;
    }
    

    第一个值为 10
    第二个值为 20

    请注意,即使既没有firstvalue也没有secondvalue直接在程序中设置任何值,但两者最终都会通过使用mypointer. 它是这样发生的:

    首先,mypointer使用地址运算符 ( &) 分配第一个值的地址。然后, 指向的值mypointer被赋值为10。因为此时mypointer正指向 的内存位置firstvalue,这实际上修改了 的值firstvalue。

    为了证明一个指针在程序的生命周期内可能指向不同的变量,这个例子用secondvalue同一个指针重复了这个过程,mypointer.

    这是一个更详细的示例:

    // more pointers
    #include 
    using namespace std;
    
    int main ()
    {
      int firstvalue = 5, secondvalue = 15;
      int * p1, * p2;
    
      p1 = &firstvalue;  // p1 = address of firstvalue
      p2 = &secondvalue; // p2 = address of secondvalue
      *p1 = 10;          // value pointed to by p1 = 10
      *p2 = *p1;         // value pointed to by p2 = value pointed to by p1
      p1 = p2;           // p1 = p2 (value of pointer is copied)
      *p1 = 20;          // value pointed to by p1 = 20
      
      cout << "firstvalue is " << firstvalue << '\n';
      cout << "secondvalue is " << secondvalue << '\n';
      return 0;
    }
    

    第一个值为 10
    第二个值为 20

    每个赋值操作都包含一个关于如何读取每一行的注释:即用&“address of”替换和号(*),用“指向的值”替换星号()。

    请注意,存在带有指针p1and的表达式p2,无论是否带有取消引用运算符( )。使用取消引用运算符()的表达式的含义与不使用的表达式的含义非常不同。当 this 操作符在指针名之前时,表达式指的是被指向的值,而当指针名出现时没有 this 操作符时,它指的是指针本身的值(即指针所指向的地址)。

    可能引起您注意的另一件事是以下行:

    int * p1, * p2;
    

    这声明了前面示例中使用的两个指针。但请注意,每个指针都有一个星号 ( ),以便两者都有类型int(指向 的指针int)。由于优先规则,这是必需的。请注意,如果代码是:

    int * p1, p2;
    

    p1确实是 type int*,但p2会是 type int。为此目的,空间根本不重要。但无论如何,对于大多数有兴趣在每个语句中声明多个指针的指针用户来说,只需记住每个指针放一个星号就足够了。甚至更好:对每个变量使用不同的语句。

  • 相关阅读:
    【JAVA】优先级队列(堆)
    背后的力量 | 搭建新型IT基础架构 华云数据助力妇幼保健院提升数字化医院建设水平
    Javaweb之SpringBootWeb案例之 Bean管理的第三方Bean的详细解析
    2022-08-20-网易笔试题
    数据库错误知识集3(摘)
    web渗透工具
    微信小程序常用学习(一)内置组件
    腾讯云将系统盘扩容到150G,怎么挂载上去
    ib中文诗歌赏析,诗歌主题怎么入手?
    验证服务器网络端口是否可访问
  • 原文地址:https://blog.csdn.net/weixin_55804957/article/details/127041503