zoukankan      html  css  js  c++  java
  • 清华大学《C++语言程序设计基础》线上课程笔记04---指针

    指针

    static int i;
    static int* ptr = &i;
    
    • 此处的*表示ptr是指针类型(地址类型),用来存放目标数据的地址
    • 其本身也有地址,所以又指向指针的指针;
    • *前面的 int 代表其指向的数据类型是 int 型,从目标i的起始单元地址取 int 数据类型字节长度的内容进行处理;
    *ptr=3;
    
    • 此处的 * 表示指针运算,即寻址过程,按照地址寻找数据单元;
      其逆运算为 & 地址运算,即返回数据单元的起始地址.

    指针变量的初始化

    定义变量后不进行初始化,会默认存储垃圾数据;
    指针变量必须存储合法取得的地址;

    int a;  //1.
    int *pa = &a; 
    

    1.用变量地址作为初值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致;
    2.可以用一个已有合法值的指针去初始化另一个指针变量;(没找到例子)
    3.不要用一个内部(局部)非静态变量去初始化 static 指针。(局部变量消亡后原本的地址就没有了意义,或者存储了其他数据)

    指针变量的赋值

    向指针变量赋的值必须是地址常量或变量,不能是普通整数,
    例如:
    1.通过地址运算“&”求得已定义的变量和对象的起始地址;
    2.动态内存分配成功时返回的地址.

    • 允许定义或声明指向void类型的指针。该指针可以被赋予任何类型对象的地址,但只用来存放地址,不能进行指针运算.
    void *general;
    
    //void类型指针的使用
    
    int main() {
    //!void voidObject; 错,不能声明 void 类型的变量,编译器无法分配存储区域大小
    void *pv; //对,可以声明void类型的指针
    int i = 5;
    pv = &i; //void类型指针指向整型变量
    int *pint = static_cast<int *>(pv); //void指针转换为int指针
    cout << "*pint = " << *pint << endl;
    return 0;
    }
    

    P.S.空指针

    int *p=0;
    double *q=NULL; //这两种为旧时代的用法,有隐藏 BUG
    
    float *a=nullptr;//C++11标准后的安全空指针
    

    指向常量的指针

    指针存储的地址可以更改,但不能改变所指向的对象的值

    int a;
    const int *p1 = &a; //p1是指向常量的指针
    int b;
    p1 = &b; //正确,p1本身的值可以改变
    *p1 = 1; //编译时出错,不能通过p1改变所指的对象
    

    指针类型的常量

    若声明指针常量,则指针本身的值不能被改变。

    int a;
    int * const p2 = &a;
    p2 = &b; //错误,p2是指针常量,值不能改变
    

    指针的算术运算

    short a[4];
    short* p=a;  //数组名便是数组首地址a[0]
    
    *(p+2)等同于a[2];
    
    p++后指针往后移动一个short类型长度,读取下一个short类型数据;
    
    • 运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置;
    • 当指针指向连续存储的同类型数据时,指针与整数的加减运和自增自减算才有意义。
      因为如果是单个变量,算术运算后移动了n个数据类型的长度,取到的是无意义数据.

    指针类型的关系运算

    • 指向相同类型数据的指针之间可以进行各种关系运算;
    • 指向不同数据类型的指针,以及指针与一般整数变量之间的关系运算是无意义的;

    P.S.可以和零之间进行等于或不等于的关系运算,来判断是不是空指针.

    例如:p==0或p!=0
    

    用指针访问数组元素

    int a[10], *pa;
    pa=&a[0]; 或 pa=a;
    

    pa就是a[0],(pa+1)就是a[1],... ,*(pa+i)就是a[i];

    a[i], *(pa+i), *(a+i), pa[i]都是等效的。
    
    int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    
    1.
    for (int i = 0; i < 10; i++)
    cout << a[i] << " ";
    
    2.
    for (int *p = a; p < (a + 10); p++)  //此处a为首地址,a+10此处运算类似指针的算术运算,是地址往后移动10个a类型的长度
    cout << *p << " ";
    
    3.
    for (int i = 0; i < 10; i++)
    cout << *(a+i) << " ";
    
    4.
    for (int *p = a,i=0; i<10; i++)
    cout << p[i] << " ";
    

    指针数组

    int main() {
    int line1[] = { 1, 0, 0 }; //矩阵的第一行
    int line2[] = { 0, 1, 0 }; //矩阵的第二行
    int line3[] = { 0, 0, 1 }; //矩阵的第三行
    
    int *pLine[3] = { line1, line2, line3 }; //定义整型指针数组并初始化
    
    //输出矩阵
    for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++)
    cout << pLine[i][j] << " ";  //此处pLine[1]等价于数组名line1,所以可以套用“数组名+下标”的方式表示数组中的某一个数据,即pLine[0][1]等价于line1[1];
    cout << endl;
    }
    

    指针数组与二维数组的显著区别在于:

    • 二维数组的每一个行都是等长的;
    • 而指针数组是用多个一维数组进行堆砌,形成一个类似二维数组的集合,每一行可以不等长;

    以指针作为函数参数

    为什么需要用指针做参数?

    1.需要数据双向传递时(引用也可以达到此效果)

    用指针作为函数的参数,可以使被调函数通过形参指针存取主调函数中实参指针指向的数据,实现数据的双向传递
    

    2.需要传递一组数据,只传首地址运行效率比较高;

    实参是数组名时,形参可以是指针
    

    和引用一样,如果只想读取数据而不想让其更改数据,可以使用指向常量的指针

    const int* p;
    

    指针类型的函数

    若函数的返回值是指针,该函数就是指针类型的函数

    int* function();
    
    • 不要将非静态局部地址用作函数的返回值(非静态局部变量返回时已经消亡);
    • 返回的指针要确保在主调函数中是有效、合法的地址;
      比如:

    1.主函数定义的数组;
    2.在子函数中通过动态内存分配new操作取得的内存地址,但要记得在主函数中进行delete;

    函数指针

    函数为:
    int example(int a)
    
    指向该函数的指针为:
    int (*function)(int)   //名字可以随便起
    
    p.s.与指针类型的函数区别在于:  
    将*和函数名包含起来的小括号()+后面小括号里的参数类型
    int *function();
    int* function() //返回int*,即int型指针的函数
    

    指针保存内存地址;
    函数的代码在内存中拥有地址;
    所以可用指针存取函数代码首地址,并据此指向函数.

    函数指针的典型用途——实现函数回调

    int compute(int a, int b, int(*func)(int, int))
    { return func(a, b);}
    
    int max(int a, int b)
    { return ((a > b) ? a: b);} 
    
    int min(int a, int b)
    { return ((a < b) ? a: b);}
    
    int sum(int a, int b)
    { return a + b;}
    
    
    res = compute(a, b, & max);//将函数代码首地址传给函数指针
    res = compute(a, b, & min);
    res = compute(a, b, & sum);
    
    

    对象指针

    Point a(5,10);
    Piont *ptr;
    ptr=&a;
    
    对象指针名->成员名
    例:ptr->getx() 相当于 (*ptr).getx();
    

    this 指针

    • 指向当前对象自己;
    • 隐含于类的每一个非静态成员函数中;
    • 当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针。
    例如:Point类的getX函数中的语句:  
    return x;  
    相当于:  
    return this->x;  //指向调用该函数的类的实例化对象
    

    动态内存分配

    指针不可替代的作用

    动态申请内存操作符 new

    • new 类型名T(初始化参数列表)
    Point *ptr1 = new Point(1,2); 
    
    • 在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以
      初值。
    • 结果值(不一定成功):成功:T类型的指针,指向新分配的内存;失败:抛出异常。

    释放内存操作符 delete

    释放指针p所指向的内存

    分配和释放动态数组

    写程序时不知道要用到的数据规模有多大时,可以动态创建数组,用完后主动释放;

    new 类型名T [ 数组长度 ];
    
    delete[] 数组名p
    
    例子:
    Point *ptr = new Point[2]; //创建对象数组
    ptr[0].move(5, 10); //通过指针访问数组元素的成员,首地址名+下标
    ptr[1].move(15, 20); 
    delete[] ptr; //删除整个对象数组
    
    

    动态创建多维数组

    new 类型名T[第1维长度][第2维长度]…;
    
    例子1:
    char (*fp)[3];  //去掉第一个[],留下剩下的值
    fp = new char[2][3]; //fp获得第一行的首地址, fp+1 指向第二行的首地址
    
    
    例子2:
    int (*cp)[9][8] = new int[7][9][8];
    
    for (int i = 0; i < 7; i++)
        for (int j = 0; j < 9; j++) 
            for (int k = 0; k < 8; k++)
                cout << cp[i][j][k] << " ";
    
    delete[] cp;
    

    将动态数组封装成类(可用vector代替该功能)

    • 更加简洁,便于管理;
    • 可以在访问数组元素前检查下标是否越界
    class ArrayOfPoints { //动态数组类
    public:
    ArrayOfPoints(int size) : size(size){  //构造函数
    points = new Point[size];  //创建动态数组
    }
    
    ~ArrayOfPoints() {  //析构函数
    cout << "Deleting..." << endl;
    delete[] points;
    }
    
    Point& element(int index) {  //返回引用可以用来操作封装数组对象内部的数组元素,返回值则只是一份副本
    assert(index >= 0 && index < size);  //检查是否越界
    return points[index];
    }
    
    private:
    Point *points; //指向动态数组首地址
    int size; //数组大小
    }
    
    
    int count;
    cout << "Please enter the count of points: ";
    cin >> count;
    ArrayOfPoints points(count); //创建数组对象
    points.element(0).move(5, 0); //对象.move()
    points.element(1).move(15, 20); 
    

    智能指针(C++11)

    - unique_ptr :不允许多个指针共享资源,指针地址不能被复制,但可以用标准库中的move函数转移到其他指针中,转移后原指针被清空.
    - shared_ptr :多个指针共享资源
    - weak_ptr :可复制shared_ptr,但其构造或者释放对资源不产生影响
    - 仅作了解;
    
  • 相关阅读:
    【原】Windows下常用命令
    【转】Samba配置文件详解
    JS笔记-选项卡的重用
    canvas.toDataURL()跨域问题
    Adobe Air 写文件如何换行
    AS3多线程快速入门(三):NAPE物理引擎+Starling[译]
    AS3多线程快速入门(二):图像处理[译]
    AS3多线程快速入门(一):Hello World[译]
    使用FileStream对象读写文件(转)
    Adobe Air写配置文件
  • 原文地址:https://www.cnblogs.com/j-c-y/p/9819270.html
Copyright © 2011-2022 走看看