05. 复合数据类型——指针

指针

  1. 指针和自由存储空间

    1. 指针是一个变量,存储的是值的地址,而不是值的本身

      • 例子:找到常规变量的地址

        #include <iostream>
        int main()
        {
            using namespace std;
            int donuts = 6;
            double cups = 4.5;
            
            cout << "甜甜圈的值是:" << donuts << endl;
            cout << "甜甜圈的地址是:" << &donuts << endl;  // 使用地址运算符&可以获得变量的地址 
            
            cout << "小蛋糕的值是:" << cups << endl;
            cout << "小蛋糕的地址是:" << &cups << endl;  
            return 0;
        }
        
    2. 新思路就是把地址视为指定的量,将值视为派生量,指针用于存储值的地址,所以指针名表示的是地址

    3. *运算符被称为间接值或解除引用运算符,将这个运算符应用于指针,可以得到这个地址处存储的值

      • 例子

        #include <iostream>
        int main()
        {
            using namespace std;
            int updates = 6;  // 声明一个变量 
            int * p_updates;  // 声明一个int类型的指针 
            
            p_updates = &updates;  // 给指针赋值(赋的是地址) 
            
            cout << "updates的值是:" << updates<< endl;
            cout << "p_updates的值是:" << *p_updates<< endl;
            
            *p_updates = *p_updates +1;
            cout << "p_updates新的值是:"<< *p_updates << endl;
            return 0;
        }
        
        • 变量updates表示值,使用&运算符来获取地址
        • 变量p_updates表示地址,使用*运算符来获得值
        • 变量p_updates执行updates,所以*p_updates和updates完全等价,可以像使用int变量那样使用*p_updates
    4. 声明和初始化指针

      • 声明指针的时候必须指定指针指向的数据类型

        int * p_updates; // *两边的空格可选,但是对于每个指针变量名,都需要使用一个*
        
        • p_updates指向int类型
        • p_updates的类型是指向int的指针
        • 所以p_updates是指针地址,但是*p_updates是int,不是指针
      • 例子:将指针初始化为一个地址

        # include <iostream>
        int main()
        {
            using namespace std;
            int higgens = 5;
            int * pt = &higgens;
            
            cout << "higgens的值是:"<< higgens << endl;
            cout << "higgens的地址是:" << &higgens << endl;
            
            cout << "*pt的值是:" << *pt << endl;
            cout << "pt的值是:" << pt << endl; 
            return 0;
        }
        
        • higgens和*pt的值一样
        • higgens的地址值和pt的值一样
    5. 指针和数字

      • 指针描述的是位置,将两个地址相乘没有意义,所以不能简单的将整数赋给指针
    6. 使用new来分配内存

      • 在运行阶段,指针可以用来分配未命名的内存来存储值,这种情况下,只能通过指针来访问内存

      • 使用new运算符来分配内存

        int * pn = new int;
        
        • new int表示需要适合存储int的内容
        • new运算符根据类型来确定需要多少字节的内存,然后找到满足要求的内存返回地址
        • 当new运算符找到地址之后,这个地址会被赋给pn
        • pn被声明为一个指针,指向int
        • *pn是存储在那里的值
      • 例子

        # include <iostream>
        int main()
        {
            using namespace std;
            int abc = 1001;
            int * pointer = new int;
            *pointer = 1001;
            
            cout << "abc的值是:";
            cout << abc << endl;
            cout << "abc的地址是:";
            cout << &abc << endl;
            
            cout << "int的值是:"<< * pointer << endl;
            cout << "int的地址是;" << pointer << endl;
            
            double * pointer_double = new double;
            * pointer_double = 1001.1;
            
            cout << "double的值是:"<< * pointer_double << endl;
            cout << "double的地址是;" << pointer_double << endl;
            
            cout << "pointer的大小是:" << sizeof(pointer) << endl;
            cout << "*pointer的大小是:" << sizeof(*pointer) << endl;
            
            cout << "pointer_double" << sizeof(pointer_double) << endl;
            cout << "*pointer_double的大小是" << sizeof(*pointer_double) << endl;
            
            
            return 0;
        }
        
    7. 使用delete释放内存

      • delete可以在使用完内存之后,将内存归还到内存池

      • 语法例子

        int * ps = new int;
        
        delete ps;
        
        • 是否ps执行的内存,但是不会删除指针ps本身,此时如果需要,可以将它重新指向另一个新分配的内存块
        • new和delete必须配对使用,不然会内存泄漏(memory leak),这样会导致被分配的内存不能再使用了
        • 不要去尝试释放已经释放的内存块
        • 不要创建两个指向同一个内存块的指针
    8. 使用new来创建动态数组

      • 通过声明创建数组的时候,在程序被编译的时候就给数组分配了内存,不管这个数组有没有被使用,都在在那儿占着内存,这种在编译时给数组分配内存的操作叫做静态联编(static binding)

      • 使用new 的时候,如果在运行阶段需要数组,就创建数组,如果不需要就不创建,也可以在程序运行的时候选择数组长度,这种操作叫做动态联编(dynamic binding)。这样创建的数组叫做动态数组(dynamic array)

      • 使用new创建动态数组

        int * pointer = new int[10];  // 创建一个包含10个int元素的数组
        
        delete [] pointer;  // 释放动态数组
        
        • new运算符返回第一个元素的地址
        • 释放动态数组的时候,方括号告诉程序,应该释放整个数组
        • 如果使用new 的时候没有方括号,那么delete的时候也不使用
      • new和delete的规则:

        • 不要使用delete释放不是new分配的内存
        • 不要使用delete释放同一个内存块两次
        • 如果使用new[]为数组分配内存,那么应该使用delete[]释放内存
        • 使用使用new[]给一个实体分配内存,那么使用delete释放(不要加方括号)
        • 对空指针应用delete是安全的
      • 使用动态数组

        • 例子

          # include <iostream>
          int main()
          {
            using namespace std;
            double * pointer = new double[3];
            pointer[0] = 0.2;
            pointer[1] = 0.6;
            pointer[2] = 0.3;
            
            cout << "pointer[1]的值是:" << pointer[1] << endl;
            pointer = pointer +1;
            
            cout << "新的pointer[0]的值是:" << pointer[0] << endl;
            cout << "pointer[1]的值是:" << pointer[1] << endl;
            pointer = pointer -1;
            delete []pointer;
            return 0;
          }
          
  1. 指针,数组,指针算术

    1. 例子

      #include <iostream>
      int main()
      {
        using namespace std;
        double a[3] = {100.1,202.5,303.3};
        short b[3] = {3,2,1};
        
        // 两种获取数组地址的方法
        double *pa = a;  // 将pa声明为指向double类型的指针,然后初始化为a数组中第一个元素的地址
        short * pb = &b[0];
        
        cout << "pa = " << pa <<", *pa = " << *pa << endl; 
        pa = pa+1;
        cout << "pa指针加1\n"; 
        cout << "pa = " << pa <<", *pa = " << *pa << endl; 
        
        pa = pa+1;
        cout << "pa指针加1\n"; 
        cout << "pa = " << pa <<", *pa = " << *pa << endl; 
        
        cout << "pb = " << pb <<", *pb = " << *pb << endl; 
        cout << "pb指针加1\n";
        cout << "pb = " << pb <<", *pb = " << *pb << endl; 
        
        cout << "a[0]的值是:" << a[0]<< endl;
        cout << "a[1]的值是:" << a[1]<< endl;
        
        cout << "*b的值是:" << *b << endl;
        cout << "*(b+1)的值是:" << *(b+1) << endl;
        
        cout << "a的数组长度是:" << sizeof(a)<< endl;
        cout << "pa的长度是:" << sizeof(pa) << endl;
         
        return 0;
      }
      
      • pa是地址,*pa是存储在这个地址上的值
    2. 使用new创建动态结构

      • 和动态创建数组一样,内存是在运行时分配的

      • 例子

        // 创建一个未命名的inflatable类型,然后将地址赋给一个指针
        inflatable * pointer = new inflatable
        
      • 例子:使用new创建一个未命名的结构体,并且使用两种方法访问结构体成员

        # include <iostream>
        struct inflatable
        {
            
            char name[20];
            float volume;
            double price;
        };
        
        int main()
        {
            using namespace std;
            inflatable * pointer = new inflatable;  // 给结构体分配内存 
            cout << "输入结构体中的name成员的值:";
            
            cin.get(pointer->name,20);  // 第一种访问成员的方法
            
            cout << "输入结构体中的volume成员的值:";
            cin >> (*pointer).volume; // 第二种访问成员的方法
            
            cout << "输入结构体中的price成员的值:";
            cin >> pointer->price;
            
            // 第二种方法的输出 
            cout << "name:"<< (*pointer).name << endl; 
            
            // 第一种方法的输出 
            cout << "volume:" << pointer-> volume<< endl; 
        
            cout << "price: $" << pointer-> price<< endl;
            delete pointer;
            return 0;
        }
        
    3. 自动存储,静态存储,动态存储

      • 自动存储
        • 在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable)
        • 这些变量在函数被调用的时候自动产生,函数结束时消亡
        • 自动变量是局部变量,作用域是包含它的代码块
      • 静态存储
        • 整个程序执行期间都会存在的存储方式,使变量变成静态的方式有两种:在函数外面定义;在声明的时候使用static关键字
      • 动态存储
        • new和delete运算符提供的方法
        • new和delete管理一个内存池,这个内存池中,用于静态变量和动态变量的内存是分开的,这样数据的生命周期就不完全受程序或函数的生存时间控制

数组的替代品

  1. 模板类vector

    1. 类似于string类,也是一种动态数组

    2. 可以在运行阶段设置vector对象的长度,可以在末尾附加新数据,也可以在中间插入新数据

    3. 这个类也是使用new和delete来管理内存

    4. 例子

      #include <vector>  // 创建vector对象需要包含头文件vector
      using namespace std;
      vector<int> vi;  // 创建一个长度为0的int数组
      int n;
      cin >> n;
      vector<double> vd(n); // 创建一个长度为n的double数组
      
  2. 模板类array

    1. c++11新增的模板类,也位于名称空间std中

    2. array对象的长度也是固定的,使用静态内存分配

    3. 效率和数组相同,但是更方便,更安全

    4. 例子

      #include <array>  // 创建array对象需要包含头文件array
      using namespace std;
      array<int,5> ai;  // 创建一个包含五个对象的int数组
      array<double,4> ad = {1.2,2.3,4.5,6.7};
      
  3. 比较数组,vector对象和array对象

    1. 无论是数组,vector对象和array对象,都可以使用标准数组的表示方法来访问各个元素

    2. array对象和数组存储在相同的内存区域中(栈)

    3. vector对象存储在另一个区域中(自由存储区或堆)

    4. array对象可以被赋值给另一个array对象

    5. 对于数组,必须逐元素去赋值数据

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容