zoukankan      html  css  js  c++  java
  • C++指针详解

    指针是啥

    众所周知,你在程序中向计算机申请一个变量(如int a),计算机会分配给你的变量一个空间。用int a举例子,计算机就给你了一个名叫a的房子,只能在里面放int类型的值。当然,你可以直接写a=10,这样计算机会帮你找到名叫a的房子,把10放进去。
    那么这个房子在哪呢?就有了地址,每个变量都会有一个地址(即它在内存中的位置),指针,便是一根棍子,指着这个位置,指针里存的就是这个地址。

    怎么用

    声明

    在类型后面加上*即可,当然指针也是分类型的,如:

    int* p;//一个指向(还没确定指向哪里)int类型房子的指针p
    double* k;//指向double的k

    如果你不确定这个类型,可以用void:

    void* p;

    但是,你如果这样写:

    int* p,q;

    事实上你是定义了一个指针和一个普通变量,所以我通常这样写:

    int *p,*q;

    注意定义指针后指针会指向一个随机的位置,如果你对这个位置进行操作,就有可能发生内存错误。

    操作

    存入地址

    这里需要用到一个新运算符:&,它称为取地址符,用于获取一个变量的地址。
    例如:

    int *p;
    int a;
    p=&a;

    你应该发现,scanf后面如果输入一个变量要用&,所以scanf后面的参数就是一个地址。
    你甚至可以输出一个地址,如果用cout就直接输出p即可,用printf需要用到格式控制符%p,如:

    int a;
    scanf("%d",&a);
    printf("%p",&a);

    每个电脑上的结果可能会不一样,例如我的:

    输出该地址上的值

    如果你想知道指针所指向位置的值,需要再次用到*

    int *p;
    int a;
    p=&a;
    a=10;
    printf("%d
    ",*p);
    a=15;
    printf("%d
    ",*p);

    运行结果是什么呢?
    这里写图片描述
    不难理解,因为一个变量的地址永远不会变。

    加/减

    你可能会想,指针是不是也有+,-操作呢?答案是肯定的,但不是把这个位置的值+,-,而是把地址往后或往前移动。
    可以做一个实验:

    #include<cstdio>
    int main()
    {
        int a=1,*p,*q;
        p=&a;
        q=p+1;
        printf("%p %p",p,q);
    }

    结果:这里写图片描述
    为什么多了4呢?因为int类型是4个字节,所以加int类型的1会往后移4个字节。
    到这里很想问了,那加char类型的'1'会怎么样?

    #include<cstdio>
    int main()
    {
        int a=1,*p,*q;
        p=&a;
        q=p+'1';
        printf("%p %p",p,q);
    }

    结果:

    并不是只移了1位,因为这里的指针q是int类型的,你的'1'被自动转为了ASCII码,所以后移了很多位。
    应该这样做:

    #include<cstdio>
    int main()
    {
        char a='1',*p,*q;
        p=&a;
        q=p+1;
        printf("%p %p",p,q);
    }

    这里的+1就成了char的后移一位,也就是1个字节,结果:

    指针数组

    很好理解:

    int *p[10];

    声明一个有十个指针(p[0],p[1],p[2],…,p[9])的数组,每个元素都是一个指针。

    其他

    scanf

    要输入n(n≤1000000)个数存入a数组再输出,一般我们会这样写:

    #include<cstdio>
    #define MAXN 1000000
    int a[MAXN+5],n;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
            printf("%d ",a[i]);
    }

    知道了指针,我们有了更装逼的方法:

    #include<cstdio>
    #define MAXN 1000000
    int a[MAXN+5],n;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",a+i);
        for(int i=1;i<=n;i++)
            printf("%d ",*(a+i));
    }

    结果:

    指针与其他数据结构

    指针与数组

    我们知道,当int a[10]时,系统会连续开10个空间,所以我们可以这样访问数组的元素:

    #include<cstdio>
    int main()
    {
        int a[10]={0,2,4,6,8,10,12,14,16,18};
        int *p;
        for(p=&a[0];p<&a[0]+10;p++)
            printf("%d ",*p);
    }

    结果就不截图了。
    其中有一个:p<&a[0]+10是什么意思呢?
    不难理解,10是数组的大小,&a[0]是数组的首地址,&a[0]+10就是数组末尾地址的下一位
    这里的p还有一个名称,叫做数组的迭代器,我们不说这些东西。
    其实获取数组首地址还有一个办法,直接这样:

    int a[10];
    int *p;
    p=a;

    这样就可以了,事实上,数组你可以看成一个指针,可以这样写:

    #include<cstdio>
    int a[10]={0,2,4,6,8,10,12,14,16,18};
    int main()
    {
        int *p;
        p=a;
        printf("%d
    ",*a);
        printf("%d
    ",*(a+5));//注意加括号,否则就是输出“a指向的值加5”了
        printf("%d
    ",*p);
        printf("%d
    ",*(++p));
    }

    结果:

    相反的,指针也可以看做一个数组,所以经常有这样写的:

    int sum(int *a,int n)
    {
        int ans=0;
        for(int i=1;i<=n;i++)
            ans+=a[i];
        return ans;
    }

    那么可以这样用:

    #include<cstdio>
    int sum(int *a,int n)
    {
        int ans=0;
        for(int i=1;i<=n;i++)
            ans+=a[i];
        return ans;
    }
    int main()
    {
        int A[10],n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&A[i]);
        printf("%d
    ",sum(A,n));
    }

    结果:

    函数指针

    你需要知道,在一个程序中,不仅仅是变量需要分配内存,函数也一样,那么函数自然也可以有指针,是函数的入口地址。函数指针声明只比函数声明多一个*一对括号,例如:

    int (*Psum)(int*,int)

    其中吧*Psum括起来的括号一定不能少,不然编译器会认为你声明了一个叫Psum的函数,返回类型是int*。
    也就是说可以这样用:

    #include<cstdio>
    int (*Psum)(int*,int);
    int sum(int *a,int n)
    {
        int ans=0;
        for(int i=1;i<=n;i++)
            ans+=a[i];
        return ans;
    }
    int main()
    {
        int A[10],n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&A[i]);
        Psum=sum;//不用'&',因为这里函数没有括号视为是函数的地址
        printf("%d
    ",(*Psum)(A,n));
    }

    到了这里,我们就知道sort是如何把函数作为函数的参数的了,可以拟出一个sort(只针对int数组):

    void sort(int *begin,int *end,bool (*cmp)(int,int))
    {
        /*......*/
    }

    结构体指针

    关于结构体,请看:C++重载运算符详解
    和声明一般指针一样,名称前加*即可。
    要访问这个指针所指向结构体当中的元素有2种方法:
    1.’*’法:在指针前加上*,再把它们括起来,就可以当该指针指向的结构体用了。
    2.’->’法,和一般结构体用法一样,只是把成员运算符.变为->即可。
    详见示例:

    #include<cstdio>
    struct student
    {
        int snum;
        int age,grade;
    };
    student T;
    student *p;
    int main()
    {
        p=&T;
        T.snum=15;
        T.age=12;
        T.grade=90;
        printf("%d
    ",(*p).snum);
        printf("%d
    ",p->age);
        printf("%d
    ",T.grade);
    }

    结果:

    多重指针

    和数组一样,你可以在声明时连续打2个(或多个)*,例如:

    #include<cstdio>
    int main()
    {
        int **p,*q;//p就是指向一个指针的指针,q是指向一个普通变量的指针
        int a;
        a=1;
        q=&a;
        p=&q;
        printf("%p  %p  %p  %p
    ",p,*p,q,&a);
        printf("%d  %d  %d",**p,*q,a);
    }

    猜猜结果:
    这里写图片描述
    前面的地址有可能会不一样,但输出的格式肯定是:
    Q P P
    A A A
    为什么*p要用%p输出?因为*pq,而q是一个指针,所以用%p

  • 相关阅读:
    apache-kylin 权威指南—读书笔记
    数据仓库之数据仓库环境——读书笔记
    R 语言—基本绘图
    MapReduce 过程分析
    HDFS 的运行机制
    R 语言贷款月供数据分析
    UML 简介笔记
    为什么要学习 UML?
    scrum 项目的基本模式
    elasticsearch 集群配置
  • 原文地址:https://www.cnblogs.com/LinqiongTaoist/p/7203725.html
Copyright © 2011-2022 走看看