深度理解指针

指针
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。


重点介绍以下内容:


指针数组

首先我们知道:

int a[] = {1,2,3,4};//一维数组
int n = 10;
int *p = &n;//一级指针
int arr[3][4] = {0};//二维数组
int **pp = &p;//二级指针

有了这些概念,那我们来讨论一下什么是指针数组?到底是数组还是指针呢?
(指针数组是数组,是一个存放指针的数组)
通过下面的例子来具体了解一下:

int *arr1[3];//存放整形指针的数组
char *arr2[4];//存放字符型指针的数组
char **arr3[5];//存放字符型二级指针的数组

解释:arr1先与[]结合,说明arr1是一个数组,数组每个元素的类型是int *(整形指针)。


数组指针

我们已经熟悉:
整形指针:int *p;(能够指向整形数据的指针)
浮点型指针:float *pf;(能够指向浮点型数据的指针)
那么数组指针是什么?指针?数组?
(数组指针是指针,能够指向一个数组的指针)

int (*p)[10];

解释:p先和*结合,说明p是一个指针变量,指向的是有10个元素的整型数组。(注意:[]的优先级要高于*,所以必须加()来保证p先和*结合)

数组指针的使用:
先来分析一段代码:

int arr[10] = {0};
arr;//数组首元素的地址
&arr;//数组的地址
//二者有何不同?
printf("%p\n",arr);
printf("%p\n",arr+1);//跳过一个元素
printf("%p\n",&arr+1);//跳过整个数组
//数组的地址和数组首元素的地址值是相同的,但是意义不同。
  • 存放数组的地址
int arr[10] = {0};
int (*p)[10] = &arr;
  • 二维数组传参
void print(int arr[3][5])
{}
void print(int arr[][5])
{}
void print(int (*arr)[5])//形参为数组指针
{}

int main()
{
    int arr[3][5] = {0};
    print(arr);//调用print函数
    return 0;
}

数组传参、指针传参

  • 一维数组传参
void test_1(int arr[])
{}
void test_1(int arr[10])
{}
void test_1(int *arr)
{}
void test_2(int *arr[20])
{}
void test_2(int **arr)//传来的参数为首元素的地址,首元素为整形指针,整形指针的地址是一个二级指针,可以用二级指针来接收
{}

int main()
{
    int arr1[10] = {0};//一维数组
    int *arr2[20] = {0};//指针数组
    test_1(arr1);
    test_2(arr2);
    return 0;
}
  • 二维数组传参
void test(int arr[3][5])
{}
void test(int arr[][5])//只能省略第一个[]的数字,因为对一个二维数组来说,可以不知道有多少行,但必须知道一行有多少个元素
{}
void test(int (*arr)[5])//传来的参数为首元素的地址,就是第一行的地址,所以用数组指针来接收
{}

int main()
{
    int arr[3][5] = {0};//二维数组
    test(arr);
    return 0;
}
  • 一级指针传参
void test(int *p)
{}

int main()
{
    int arr[10] = {0};
    int *p = &arr;
    int mun = 10;
    test(arr);
    test(p);
    test(&num);
    return 0;
}
  • 二级指针传参
void test(int **pp)
{}

int main()
{
    int num = 10;
    int *p = #
    int **pp = &p;
    int *arr[10];//指针数组
    test(pp);
    test(&p);
    test(arr);
    return 0;
}

函数指针

函数指针是用来存放函数的地址

void (*p)();

解释:p先与*结合,说明p是指针,指向的是一个函数,函数无参数,返回类型为void。
举个例子:

void test()
{}

int main()
{
    void (*p)() = test;//函数指针
    (*p)();//调用test函数
    p();//调用test函数另一种写法
    return 0;
}
  • 这里来看两个有趣的代码:
//1.
(*(void(*)())0)();

//2.
void(*signal(int ,void(*)(int)))(int);
  1. 把0强制类型转换为函数指针,该函数指针没有参数,前面加*表示对0地址处的函数解引用,再调用函数。
  2. signal是一个函数声明;
    signal函数有两个参数:一个是int,一个是函数指针,该函数指针能够指向的函数有一个int的参数,函数返回类型为void;
    signal函数的返回类型为函数指针,该函数指针能够指向的函数有一个int的参数,返回类型为void。

第2个代码可以简化为:

typedef void(*pfun_t)(int);
pfun_t signal(int ,pfun_t);

函数指针数组、指向函数指针数组的指针

  • 函数指针数组是用来存放函数地址的数组
int (*p[4])(int, int);

解释:p先与[]结合,说明p是数组,数组元素为int (*)(int, int)类型的函数指针。

  • 指向函数指针数组的指针是一个指针,指向一个数组,数组的元素都是函数指针。
int (*(*p2)[4])(int, int);

解释:p2先与*结合,说明p2是指针,指向的是一个数组,数组的元素为int (*)(int, int)类型的函数指针。

  • 对比函数指针、函数指针数组、指向函数指针数组的指针
int (*p1)(int, int);//函数指针
int (*p2[4])(int, int);//函数指针数组
int (*(*p3)[4])(int, int);//指向函数指针数组的指针

可以看出:函数指针数组就是在函数指针的基础上让p2先与[]结合。
指向函数指针数组的指针就是在函数指针数组的基础上让p3先与*结合。

通过下面的例子来更好的理解这三个概念:

#include <stdio.h>
int Add(int x, int y)
{
    return x+y;
}
int Sub(int x, int y)
{
    return x-y;
}
int Mul(int x, int y)
{
    return x*y;
}
int Div(int x, int y)
{
    return x/y;
}

int main(int argc, const char * argv[])
{
    int(*p1)(int, int) = Add;//函数指针
    int (*p[4])(int, int) = {Add, Sub, Mul, Div};//函数指针数组
    int (*(*p2)[4])(int, int) = &p;//函数指针数组的指针
    int i = 0;
    for(i=0; i<4; i++)
    {
        printf("%d\n",p[i](2,2));
    }
    return 0;
}
  • 函数指针数组的用途:转移表
    来看一个例子(计算器)
#include <stdio.h>
void menu()
{
        printf("************************\n");
        printf("**    1.add    2.sub  **\n");
        printf("**    3.mul    4.div  **\n");
        printf("************************\n");
}
int Add(int x, int y)
{
    return x+y;
}
int Sub(int x, int y)
{
    return x-y;
}
int Mul(int x, int y)
{
    return x*y;
}
int Div(int x, int y)
{
    return x/y;
}

int main(int argc, const char * argv[])
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;
    int (*p[5])(int, int) = {0, Add, Sub, Mul, Div};//转移表
    do
    {
        menu();
        printf("please choose:");
        scanf("%d",&input);
        if(input>=1 && input <=4)
        {
            printf("\nplease input two numbers:");
            scanf("%d%d",&x,&y);
            ret = p[input](x,y);
            printf("ret = %d\n",ret);
        }
        else
        {
            printf("input error\n");
        }
    }while(input);
    return 0;
}

END

发布了38 篇原创文章 · 获赞 37 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Lsxlsxls/article/details/81228565