指针的深度学习(数组指针、指针数组等)

前言:本篇文章回忆了指针的基本知识,在基本知识的基础上,加深了对指针的理解,然后引出指针数组、数组指针、函数指针灯一系列概念。如果那里写的有问题,欢迎指出。

指针是什么

指针是一个变量,用来存放地址的。


    #include<stdio.h>
    int main()
    {
        int a = 10;//在内存中开辟一块空间
        int *p = &a;//&a取出变量a的地址,存到指针变量p里
        *p = 20;//对指针变量p解引用,找到a的那块空间,改变a的值
        return 0;
    }

指针的大小

  • 32位机器,地址是32个0或1组成的二进制序列,也就是32个比特位,而一个字节恰好是8个比特位,所以在32位机器上指针大小是4个字节
  • 64位机器,地址是64个0或1组成的二进制序列,也就是64个比特位,而一个字节恰好是8个比特位,所以在64位机器上指针大小是8个字节

指针的类型


    char *p=NULL;
    int  *p=NULL;
    short *p=NULL;
    float *p=NULL;
    ...

去掉变量名,剩下的就是它的类型,所以指针的类型为char * ,int * ,等等。char *类型的指针是为了存放char类型变量的地址,其他的可以以此类推。

指针的运算

  • 指针+-整数
  • 指针的解引用
  • 指针-指针

指针+-整数


    #include<stdio.h>
    int main()
    {
        int n = 10;
        char *pc = (char *)&n;
        int *pi = &n;

        printf("%p\n", &n);
        printf("%p\n", pc);
        printf("%p\n", pc+1);//1地址偏移1个字节
        printf("%p\n", pi);
        printf("%p\n", pi+1);//1地址偏移4个字节
        return 0;
    }

指针的解引用

对指针进行解引用操作能找到指针所指向的那块空间,指针的类型决定了解引用时能操作多大的空间,例如:对char *的指针解引用只能访问一个字节,而对int *指针解引用能访问4个字节。

指针-指针

指针-指针的结果是两个指针之间元素的个数,是一个整数。

例如:模拟实现库函数strlen就可以用指针-指针的方法


    int my_strlen(char *s)
    {
        char *p = s;//保留字符串的起始地址
        while (*p != '\0')
        {
            p++;
        }//循环结束,指针p指向的是'\0'
        return p - s;
        //'\0'的地址和首元素地址中间的长度刚好是字符串的长度
    }

注意:给定一个arr[10]={0};p1=&arr[0],p2=&arr[9],p2-p1是9而不是10.

二级指针

二级指针是用来存放指针的地址的

这里写图片描述

对上边的例子,对ppa解引用得到pa,对pa在进行解引用得到a的那块空间。

指针表达式解析

假设有下面代码


    char ch='a';
    char *cp=&ch;

判断下列表达式能不能做左值,能不能做右值。
这里写图片描述

回顾指针和数组的相关运算

前面写了关于数组和指针运算的一篇博客,在下面的指针和数组概念的了解很重要,所以贴出链接——>数组和指针的基本运算

数组代表整个数组有两种情况
sizeof(数组名),这里的数组名代表整个数组(一定是数组名单独放在sizof内部)
&sizeof,这里的数组名代表整个数组

指针数组

指针数组是一个数组,用来存放指针的数组

例如:int *arr[10];是一个指针数组。

注意:[]的优先级高于*,arr先和[]结合,所以它是一个数组,它的类型是int *[10],所以它是一个存放指针的数组,既指针数组

数组指针

数组指针是一个指针,用来指向数组的指针,也就是存放数组的地址

例如:int (*arr)[10];是一个数组指针

注意:[]的优先级高于,但是*arr上了括号,所以arr先和结合,arr首先是一个指针,它的类型是int (*)[10],所以它是一个指向大小为10个整型数组的指针,用来存放数组的地址

我们知道数组名代表首元素的地址,&数组名代表的是数组的地址,那么数组的地址如何存储?数组指针就是用来存放数组的地址。
例如:


    int arr[10]={0};
    int (*p)[10]=&arr;

了解了【数组指针】和【指针数组】,我们看一下下边的代码代表什么


    int arr[5];//一维数组
    int arr[3][5];//二维数组
    int *arr[10];//指针数组
    int(*arr)[10];//数组指针
    int(*arr[10])[5];
    //它首先是一个存放10个元素的指针数组,每一个指针又指向了一个存放5个元素的数组

数组和指针传参

一维数组传参


    #include<stdio.h>
    void test1(int arr[])//用一维数组接收
    void test1(int arr[10])//用一维数组接收
    void test1(int *arr)//用指针接收数组首地址
    void test2(int *arr[20])//用指针数组接收
    void test2(int **arr)//用一个二级指针接收
    int main()
    {
        int arr1[10] = { 0 };//一维数组
        int *arr2[10] = { 0 };//指针数组
        test1(arr1);
        //传参数组首元素的地址
        test2(arr2);
        //arr2是一个指针数组,存放的是指针,传参arr2代表数组首元素的地址,是指针的地址,既二级指针
        return 0;
    }

二维数组传参


    #include<stdio.h>
    void test(int arr[3][5])//用二维数组接收数组
    void test(int arr[][])//用二维数组接收数组,但是列不能省略,要知道一行有几个元素,这种方法错误
    void test(int arr[][5])//用二维数组接收数组,行可以省
    void test(int *arr)//整型指针接收二维数组不合适
    void test(int *arr[5])//这是一个指针数组,用来存放一级指针的,存放数组地址不合适
    void test(int (*arr)[5])//数组指针接收第一行数组的地址,合适
    void test(int **arr)//二级指针用来存放一级指针的地址,存放数组地址不合适
    int main()
    {
        int arr[3][5] = { 0 };
        test(arr);//二维数组名代表第一行的地址
    }

一级指针传参

一级指针传参,用一级指针接收。但当一个函数参数部分为一级指针,函数能接受什么参数

例如:


    void test(int *p)
    //test函数可以接收指向int类型的一级指针、int类型的数组、int类型变量的地址

二级指针传参

二级指针传参,用二级指针接收。但当一个函数参数部分为一级指针,函数能接受什么参数

例如:


    void test(char **p)  
    //test函数可以接受二级指针、一级指针的地址和指针数组  
    //指针数组是存放指针的数组,传参数组名是传的第一个指针的地址

函数指针

函数指针,顾名思义,就是一个指向函数的指针

例如:

void (fun)()就是一个函数指针,fun先和结合,说明fun是一个指针,然后它指向的类型为void(*)(),指向的是一个函数,这个函数无返回值,无参数。


    #include<stdio.h>
    void test()
    {
        printf("hello,world\n");
    }
    int main()
    {
        printf("%p\n", test);
        printf("%p\n", &test);
        return 0;
    }

这段代码的输出结果都是00A7107D,可以说明函数名和&函数名都代表的是函数的地址,那么函数的地址存在哪里呢?其实就是函数指针了。

注意:理解下边的两个代码

  • 代码一:(*(void(*)())0)()

首先代码(void(*)())0是将0强制装换为函数指针类型,然后对它解引用得到0地址处的函数。这个函数无参数和返回类型,这是一个函数的调用。

  • 代码二:void (*signal(int,void(*)(int)))(int)

首先这是一个是一个函数的声明,signal函数的有两个参数一个是int类型的,一个是函数指针类型的,这个函数指针有一个int类型的参数,返回值是void。然后signal函数的返回值为void(*)(int)
这也是一个函数指针,有一个int类型的参数,返回值为void。

对上边的代码二我们可以简化一下


    typedef void(*pfun_t)(int)
    pfun_t signal(int ,pfun_t)
    //将signal函数的返回类型void(*)(int)重命名为pfun_t,而signal函数的其中一个参数和signal函数的返回参数是一样的,所以可以写成上述表达式

函数指针数组

函数指针数组,首先它是一个数组,用来存放函数指针的数组,函数指针又是存放函数的地址,那么这个数组就是存放函数地址的数组

例如:


int (*parr[10])();
//parr先和[]结合,它首先是一个数组,然后它存储元素的类型是一个函数指针`int (*) ()`,这个数组存储了10个函数指针。

指向函数指针数组的指针

指向函数指针数组的指针首先是一个指针,它指向的是一个存放函数地址的数组,用来存放函数指针数组的地址。

例如:


    #include<stdio.h>
    void test(const char* str)
    {
        printf("%s\n", str);
    }
    int main()
    {
        void(*ptest)(const char*) = test;
        //定义一个函数指针ptest,它指向函数参数为const char*类型的,返回值为void,用来存放test函数的地址
        void(*ptestarr[5])(const char*);
        //定义一个函数指针数组,这个数组存放5个函数指针
        ptestarr[0] = test;
        //ptestarr[0]存放的是test函数的地址
        void(*(*pptestarr)[10])(const char*) = &ptestarr;
        //定义了一个指向函数指针数组的指针,它指向函数指针数组,存放的是函数指针数组的地址
        return 0;
    }

猜你喜欢

转载自blog.csdn.net/hansionz/article/details/80871978
今日推荐