指针和数组

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_40550018/article/details/81383829

目录

  • 指针数组
  • 数组指针
    • 数组指针的应用
  • 函数指针
  • 函数指针数组
    • 函数指针数组的应用(转移表)
  • 指向函数指针数组的指针

指针数组

指针数组,即指针的数组, 它不是指针,而是一个存放指针的数组,数组的每一个元素都是指针类型的,例如下面的都是指针数组:
int *arr[10];//指针数组,10元素,每个元素都是整型指针类型的
char *arr[4];//指针数组,4个元素,每个元素都是字符指针类型的
char **arr3[5];//指针数组,5个元素,每个元素都是二级指针类型的
 
 
  • 1
  • 2
  • 3

由于[ ]的优先级要比*的优先级高,所以数组名先和[ ]结合,所以它的本质是数组,而它每一个元素都是指针类型的,所以指针数组的每一个元素都占四个字节。

##数组指针

数组指针,即数组的指针,它不是数组,而是指向首元素地址的指针,本质是指针(这个指针存放的是数组首地址的地址,相当于二级指针,这个指针不可移动);例如:
int (*p)[10];
 
 
  • 1

解释:p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组。所以p是一个指针,指向的是一个数组,叫做数组指针。

####数组指针的使用
我们了解了数组指针的定义,那么数组的地址如何来存储?

int arr[10]={0};
int *p1=&arr;
int (*p2)[10]=&arr;
 
 
  • 1
  • 2
  • 3

这三种哪一种比较合适呢?

我们知道:
整型变量的地址应该放在整型指针里面去
字符变量的地址应该放在字符指针里面去 所以数组变量的地址应该放在数组指针里面去

显然第三种p2是比较合适的选择,p2是数组指针,所以存放数组的地址是最合适的。
举个栗子,我们看一段代码:

void print(int(*p)[5], int x, int y)//二维数组的参数(*p)[5]相当于p[][5]
{//p死一个指针,指向一个数组,这个数组有五个元素,每个元素是整型
    int i = 0;
    int j = 0;
    for (i = 0; i < x; i++)
    {
        for (j = 0; j < y; j++)
        {
            printf("%d ", *(*(p+i)+j));//打印二维数组的每个元素
        }//p是指针指向首元素地址,也就是第一行的地址,+i就是第几行的地址,解引用就是首元素地址,再+j,就是每个元素的地址,再解引用就是每个元素了
        printf("\n");
    }
}
int main()
{
    int arr[3][5] = { 1,2,3,4,5,5,4,3,2,1,6,7,8,9,0 };//二维数组
    print(arr, 3, 5);
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

运行结果如下:
这里写图片描述
数组指针的应用一般都是二维数组传参的时候用
上面我们了解了指针数组和数组指针,接下来我们辨认一下,下面每个分别是什么?

int arr[5];
int *parr1[10];
int(*parr2)[10];
int(*parr3[10])[5];
 
 
  • 1
  • 2
  • 3
  • 4
1.很简单,是我们经常见到的一维数组
2.是上面我们了解到的指针数组
3.也是上面我们知道的数组指针
那么第四个到底是什么呢?我们分析一下:
首先parr3是一个数组,因为它和[10]靠的近,数组有10个元素,再从外面看,他是一个数组指针,有5个元素,每个元素是整型,图解如下:

这里写图片描述

函数指针

通过上面的了解,我们应该进一步知道函数指针,是指向函数的指针,用来存放函数的地址,那么说明函数也是有地址的,那么我们怎么取出函数的地址呢?
我们通常取数组的地址是&加数组名,那么取函数地址,是否也可以&加函数名呢?如下:

void test()
{
    printf("hehe\n");
}
int main()
{
    printf("%p\n", &test);
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这里写图片描述
可见,取出的确实是这个函数的地址,&函数名是整个函数的地址,那么函数名难道是首元素的地址吗?显然函数我们无法谈论首元素,所以在函数取地址的时候&加函数名或是直接函数名取出的都是函数的地址,如下:

void test()
{
    printf("hehe\n");
}
int main()
{
    printf("%p\n", &test);
    printf("%p\n", test);
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这里写图片描述
了解了函数的地址,那么我们应该如何将函数的地址保存起来,这就要用到函数指针了
我们看如下代码:

void *p();//p先和()结合,所以是一个函数
void (*p)();//p先和*结合,所以是一个指针
 
 
  • 1
  • 2

第一个是函数的声明,而第二个则是函数指针,所以如果我们想把上面的test()这个函数的地址存储起来,就可以用函数指针void (*p)()来存储。下面举出函数指针定义的例子:

 int Add(int x, int y)
 {
    return x+y;
 }


 int main()
 {
    int (*p)(int, int) = Add;
    printf("%d\n", p(2,3));
    Add(3, 4);
    return 0;
 }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

解读两段有趣的代码:

//代码一:
(*(void(*)())0)();
//代码二:
void(*signal(int,void(*)(int)))(int)
 
 
  • 1
  • 2
  • 3
  • 4

代码一:首先从0入手,0前面的(void(*)())是强制类型转换,是将0强制类型转换成函数指针,再对整体解引用,是找到0地址处的函数,这个函数无参,返回类型是void。
代码二:①signal是一次函数声明
②signal函数的参数,第一个是int,第二个是函数指针,该函数指针指向的函数参数为int,返回类型为void
③signal函数的返回类型为一个函数指针,该函数指针指向的参数是int,返回类型是void

函数指针数组

上面解释了函数指针及其定义,我们知道了函数指针是存放函数地址的,那么如果我们把函数指针的地址存到一个数组中,那这个数组就叫做函数指针数组,那么函数指针数组应该如何定义呢?下面哪种定义方式是正确的?

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
 
 
  • 1
  • 2
  • 3

答案是parr1,parr1先和[ ]结合,说明parr1是数组,数组的内容则是int (*)()类型的函数指针

函数指针数组的用途:转移表

实现计算器:
方法一:函数调用
一般情况下,我们实现两个数的加减乘除会像下面这种方式写:

int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
    return a - b;
}
int mul(int a, int b)
{
    return a * b;
}
int div(int a, int b)
{
    return a / b;
}

int main()
{
    int x, y;
    int input = 1;
    int ret = 0;
    while (input)
    {
        printf("***************************************\n");
        printf("****     1:add            2:sub    ****\n");
        printf("****     3.mul            4.div    ****\n");
        printf("***************************************\n");
        printf("请选择:\n");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("请输入操作数:");
                scanf("%d %d", &x, &y);
            ret = add(x, y);
            break;
        case 2:
            printf("请输入操作数:");
            scanf("%d %d", &x, &y);
            ret = sub(x, y);
            break;
        case 3:
            printf("请输入操作数:");
            scanf("%d %d", &x, &y);
            ret = mul(x, y);
            break;
        case 4:
            printf("请输入操作数:");
            scanf("%d %d", &x, &y);
            ret = div(x, y);
            break;
        default:
            printf("选择错误\n");
            break;
        }
        printf("ret=%d\n", ret);
    }
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

方法二:使用函数指针数组

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;
}

void menu()
{
    printf("******************************\n");
    printf("**     1. add     2. sub    **\n");
    printf("**     3. mul     4. div    **\n");
    printf("**          0.exit          **\n");
    printf("******************************\n");
}
void calc(int(*pfun)(int, int))//函数指针
{
    int x = 0;
    int y = 0;
    int ret = 0;
    printf("请输入两个操作数:>");
    scanf("%d%d", &x, &y);
    ret = pfun(x, y);
    printf("ret = %d\n", ret);
}
//
int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;

    //转移表
    int(*pfun[5])(int, int) = { 0, Add, Sub, Mul, Div };
//定义一个函数指针数组,里面有5个元素,每个元素分别是一个函数
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            calc(Add);
            break;
        case 2:
            calc(Sub);
            break;
        case 3:
            calc(Mul);
            break;
        case 4:
            calc(Div);
            break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

运行结果如下:
这里写图片描述

指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针;
那么指向函数指针数组的指针又该如何定义呢?

void test(const char *str)
{  
   printf("%s\n",str);
}
int main()
{
 void(*pfun)(const char *str);//函数指针
 void(*pfunArr[5])(const char * str);//函数指针数组
 pfunArr[0]=test;
 void (*(*ppfunArr)[10])(const char *)=&pfunArr;//指向函数指针数组pfunArr的指针ppfunArr;
 return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-095d4a0b23.css" rel="stylesheet">
                </div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值