美文网首页
C++指针, since 2020-11-22

C++指针, since 2020-11-22

作者: Mc杰夫 | 来源:发表于2020-11-22 13:12 被阅读0次

    (2020.11.22 Sun)

    指针(Pointer)是什么?

    指针是一个地址,指向存储某一个数据的存储地址。指针变量是一种特殊性质的变量,它把地址存在一个变量中,通过先找出地址变量中的值(一个地址),再由此地址找到最终要访问的变量的方法,刺猬指针变量和访问方法。地址变量就是指针。

    指针除了可以指向变量之外,还可以指向内存中的任何数据结构,和函数。注意,程序中参加数据处理的量不是指针本身的量(因指针本身是一个地址量),而指针所指向的内存区域中的数据(称为指针目标)才是需要处理的数据。

    如何定义指针

    定义指针的语法形式:

    存储类型名 数据类型 *指针变量名

    int *p1;
    stat float *p2;
    char *p3;
    
    • 存储类型名:C++中的存储类型一般有静态存储、栈和自动类型三种,默认是自动类型auto。
    • 数据类型:可以是任意基本数据类型、派生类型名和自定义类型名,也可以是由<类型名>*表示的指针类型名,称为多级指针。

    指针变量前面的类型名代表着该变量所指向的数据的类型,而指针变量本身的类型是unsigned long int。

    注意定义指针过程中,比如int p1,其中的用来代表p1这个变量是指针变量,而指针变量是p1,而不是*p1。*p1表示p1所指的变量值。

    指针的初始化

    定义之后,使用之前,需要要赋一个合法的值。初始化的形式如下

    存储类型 数据类型 *指针名=初始地址

    比如将a的内存地址做为初值赋给int型指针pa

    int a, *pa = &a; //第一种表达
    int a; // 第二种表达,和前一种等价
    int *pa = &a;
    int a, *pa; //第三种表达
    pa = &a; 
    

    其中的符号&a代表了变量a的内存地址。注意数据类型和指针类型要一致。

    初始化为空指针

    int *p = 0;
    

    安全起见,定义指针时,最好初始化,哪怕是初始化为0的指针。

    下面是通过指针为所指向变量赋值的方法

    #include <iostream.h>
    void main () {
        int a = 1;
        cout << 'a=' << a << endl;
        int *pa = &a;
        *pa = 2;
        cout << 'a=' << a << endl;
    }
    

    指针的运算

    取地址和取值运算

    指针运算是以指针变量所持有的地址值作为运算量进行的运算。
    两个运算符

    • &: 取地址运算符,&x的值时x的地址
    • *: 指针运算符,指向运算符,也称为间接运算符,*p代表p所指向的变量

    针对运算符,需要明确指针变量(int *p)和符号的组合所代表的不同含义

    • p: 指针变量,它的内容(或值)是地址量
    • *p: 指针的目标变量,它的内容是(所指内容的)数据
    • &p: 指针变量p所占用的存储区域的地址
    指针的算数运算

    诸如p+n, p-n, p++, ++p, p-q等操作。

    1. 加法运算p+n,其中的n是个整数,p是指针。指针作为地址量加减的整数n,其意义是指针当前指向位置的前或后第n个数据的位置,即数据长度不同的数据,所以这种操运算的结果取决于指针指向的数据类型,而非仅仅从p所指的地址移动n个值。

    p+n的实际操作是(p) + n * sizeof (数据类型)

    其中(p)代表了指针p中的地址值而非p本身的地址(&p)。sizeof(数据类型)的长度单位是字节

    1. p++, p--, ++p, --p这些操作和加法运算相似,都是指向了下一个数据的位置,而非内存地址紧接着的下一个。如int *p的p++,如果初始时p的值(内存地址)是1000,则++p后p的值是1002(因一个int占2个字节)。
    y = *p++;
    

    根据符号运算的优先级,和++优先于=,和==属于同一优先级,其结合规则是从右向左。所以优先进行++运算,相当于

    y = *(p++);
    

    该表达式的含义是,访问p当前值指向的目标,并将目标的值赋给y,p指向下一个目标。

    1. 指针减法p-q
      这里的p和q都是指针变量,所指向的数据类型相同p-q的结果值时两只真指向的地址位置之间的数据个数
    int x[5],a;
    int *px=&x[1], *py=&x[4];
    a = py-px;
    

    返回的a表示x[1]和x[4]之间相隔的元素个数。

    验证下面代码

    #include <iostream.h>
    void main () {
        int a[5] = {1,2,3,4,5};
        int *pa, *pb;
        pa = &a[0];
        pb = &a[4];
        cout << "*pa=" << *pa <<endl;
        cout << "*pb=" << *pb <<endl;
        pa = pa+2;
        cout<<"pa+2"<<*pa<<endl;
        pa = pa++;
        cout<<"pa++"<<*pa<<endl;
        int c = pb-pa;
        cout<<"pb-pa"<<c<<endl;
    }
    
    指针的关系运算

    两个指针的比较,假设数据在内存中的存储逻辑是由前向后,指向后方的指针大于指向前方的指针,也就是说

    int a,b; #假设&b>&a
    int *pa,*pb;
    pa=&a, pb=&b;
    pb>pa; #true
    

    指针与0的比较,用于判断指针是否为空指针。

    指针的赋值

    (2020.11.23 Mon)
    当向指针变量赋值时,赋的值必须是地址常量或变量,不能是普通的整数
    赋值有三种方式如下

    • 把一个变量的地址赋予一个指向相同数据类型的指针
    char a, *p;
    p = &a;
    
    • 把一个指针的值赋给相同数据类型的另外一个指针
    float *p,*q;
    p=q;
    
    • 数组的地址赋予指向相同数据类型的指针
    char a[10], *p;
    p = a; //数组名代表了该数组的首地址
    

    此外,前面提到的指针与整数的运算也是指针赋值的形式。

    void指针和const指针

    指向void类型的指针称为void指针。用关键字const修饰的指针称为const指针。

    1. void指针。一般来说,给不同类型的指针赋值时错误的,但void指针是个特例,C++允许使用空类型(void)指针,即不指定指针指向一个固定的类型,定义为
    void *p;
    

    它表示指针变量p不指向一个确定的类型数据,它的作用仅仅是用来存放一个地址。它可以指向任何类型的C++数据。也就是说,可以用任何类型的指针直接给void指针赋值。不过如果需要将void指针的值赋给其他类型的指针,需要进行强制类型转换。

    int a;
    int *p=&a;
    void *p2 =p;
    int *p3 = (int *)p2; //void指针强制转换成int类
    
    1. const指针。指针定义加上关键字const,可根据位置不同表不同含义。
    • const放在指针类型前,声明一个指向常量的指针,此时在程序中不能通过指针来改变它所指向的值,但指针可以指向其他数据
    • const放在*和指针名之间,声明一个指针常量(常指针),指针本身的值不可改变,即它不能指向其他数据,但所指向的数据的值可以更改
    • 两处都加const,声明一个指向常量的指针常量,指针本身的值不可改变,所指向的数值也不能通过指针改变
    const int *p; //指向常量的指针p
    int * const q; //指针本身的值不可改变,但其所指的值可以
    const char * const m; //什么都不能改
    

    他们的区别如何理解:const int * p中的const是修饰int,也就是p所指向的变量,而int * const q中的const修饰q,也就是一个不可变的指针变量q,它不能指向其他数据。

    指针和数组

    数组名表示的是该数组的首地址。定义一个指向数组的指针,可以如下定义(两种方式)

    int a[10];
    int *p=a;
    int *p = &a[0]; //&a[0]是元素a[0]的地址,也是数组a的首地址
    
    访问数组元素的方法
    1. 下标,a[0], a[1], a[2], ...
    2. 地址法,数组名为数组首地址,数组中的任意元素的访问可以通过首地址的加减法来实现,比如a[3]可以是*(a+3)
    3. 指针法,类似于地址法,把首地址赋值给一个指针,通过指针的加减法寻找元素
    int a[10];
    a[3]; //下标法
    *(a+3); //地址法
    int *p = a;
    *(p+3); //指针法
    
    多维数组元素的访问

    一个二维数组比如a[4][5],其中的a[i]表示第i-1行的首地址,(地址法)推导得知a[3]等于&a[3][0],a[0]+1等于&a[0][1]。a[i][j]有多种访问法

    *(*(a+i) + j);
    *(a[i]+j);
    *(a+i)[j];
    *(a+4*i+j);
    
    指针数组和数组指针

    指针数组是其元素为指针的数组;数组指针是指向数组的指针。

    • 数组指针
      数组指针是一个指向一位数组的指针变量,定义格式为

    数据类型 ( * 指针名) [常量表达式];

    int a[5] = {1,3,5,7,9};
    int (*p)[5]=&a; //指向包含5个整型元素的一维数组的指针
    count<<"*p[0]="<<*p[0]<<endl;
    
    • 指针数组
      元素为指针的数组,语法格式为

    数据类型 *指针数组名 [常量表达式];

    int *p[6]; //[]的优先级比*高,故p先与[]结合,是数组类型,再与*结合形成指针
    

    指针数组常用于字符串的操作

    static char *name[3] = {"tom","mary","rose"};
    cout<< "name[0]="<<name[0]<<endl;
    

    name的每个元素都是指向字符数据的指针类型数据,其中name[0]指向tom,name[1]指向mary,name[2]指向rose。用指针数组处理字符串不仅可以节省内存,还可以提高运算效率,比如对5个姓名排序,字符串交换位置速度慢,而交换地址则快得多。

    指针与函数

    指针作为函数参数

    函数参数是指针,是传址调动,也就是是实参指针和形参指针变量指向同一个内存地址。形参指针所指向的地址中内容的改变也会影响实参。函数调用时传入的参数应该是一个地址。

    #include <iostream.h>
    void swap(int *, int *);
    void main()
    {
        int a =4,b=0;
        swap(&a, &b); //调用时传入的是参数的地址
    }
    void swap(int *x, int *y) // 传入指针变量
    {
        int m = *x;
        *x = *y;
        *y = t;
    } //函数的传址调用中,传递的参数的值不改变,指针本身的值并不改变,改变的是它指向的值
    
    指针型函数

    当一个函数的返回值是指针类型时,该函数是指针型函数。
    使用场景:通常非指针型函数调用结束后,可以返回一个变量,但每次都只能返回一个数据。如果需要从被调函数返回一批数据到主调函数中,就可以使用指针型函数来解决。其语法格式如下

    数据类型 *函数名(参数表)

    • 数据类型是函数返回的指针所指向数据的类型
    • *函数名声明了一个指针型函数
    • 参数表示函数的形参列表
    int *fun(int a,int b); //返回值是一个int型的指针
    
    #include <iostream.h>
    #include <string.h>
    char *max(char *a, char *b) 
    {
        return(strcmp(a,b)?a:b);
    }
    void main()
    {   
        char *p;
        p = max('hello','good');
        cout<<p<<endl;
    }
    
    函数指针

    函数指针是指向函数的指针。语法格式为

    数据类型 (*函数指针名) (参数表);

    • 数据类型是指函数指针所指向函数的返回值类型
    • 参数表中指明该函数指针所指向函数的形参类型和个数
    int (*p) (int, int);
    

    在定义了指向函数的指针变量后,在使用此函数指针之前,必须先给它赋值,使它指向一个函数的入口地址。由于函数名是函数在内存中的首地址,因此可以将函数名赋给函数指针变量,赋值的语法格式

    函数指针名=函数名;

    p = funcl;
    

    其中,函数名所代表的函数必须是一个已经定义过的,和函数指针具有相同返回类型的函数,并且等号后面只需写函数名而不要写参数。比如不能写成如下形式p = funcl(a,b)。函数指针指向某函数后,可以用下列形式调用函数

    (*指针变量)(实参表列)

    表达式(*p)(a,b)相当于funcl(a,b)。
    与定义一般变量指针数组一样,C++中可以定义具有特定返回类型和特定参数类型的函数指针数组。函数指针数组可以是多维的,在实际编程中一般只用到一维函数指针数组,语法格式如下

    数据类型 (*函数指针明[常量表达式])(参数表);

    int (*p[5])(int,int); //定义了一个含有5个元素的函数指针数组,每个元素都是一个指向函数的指针,
    // 且指向的函数都是返回值类型为整型、带两个整型参数的函数 
    

    指针和字符串

    char *str2="C++";
    

    二级指针

    声明一个指针来指向指针,这个指针称为指向指针的指针,称为二级指针。定义形式

    存储类型 数据类型 **指针变量名

    • 两个星号表示二级指针
    • 数据类型是指通过两次间接寻址后所访问的变量类型
    int i, *p=&i;
    int **pp = &p; //声明了一个指向指针的指针pp,其指向指针p
    

    Reference

    1 刘蕾编著,21天学通C++(第五版),电子工业出版社

    相关文章

      网友评论

          本文标题:C++指针, since 2020-11-22

          本文链接:https://www.haomeiwen.com/subject/ybcuiktx.html