【C语言专栏】:一级/二级指针?数值指针/指针数组?函数指针/函数指针数组?转移表的实现

在这里插入图片描述

🚩🚩前言

🎉✨欢迎来到指针汇总章节!相信各位都听说各种指针名字吧!比如:一级指针、二级指针、数组指针、函数指针。以及名字反过来又不一样的指针数组、函数指针数组等。看到这些感觉头脑都晕了😵😵,接下来慢慢梳理一下这些各种指针的定义和用法。👇👇👇👇


🔑🔑一级指针

🎈🎈定义

一级指针,也叫做普通指针。它是用来存放各种数据类型变量的地址的。但是一级指针和普通类型变量的定义也有些许区别,并且含义不一样。
指针嘛,在前面章节讲到指针即地址,指针变量就是专门用来存储地址。接下来看看一级指针:

int  a=10;
int* ptr1=&a;//这就是一级指针,*就是代表ptr1是指针变量,是存储的整型变量a的地址的。
  • 我们可以存储变量的地址,从而改变原来变量的值。
#include<stdio.h>
int main()
{
	int a = 10;
	int* ptr1 = &a;//此处*号表示ptr1是指针变量
	*ptr1 = 50;//此处的*号是解引用操作
	printf("a=%d\n",a);
	return 0;
}
  • 结果被改变:

在这里插入图片描述

其改变a的值,原理是下面图所展示的:
在这里插入图片描述


🔑🔑二级指针

🎈🎈定义

上面说到一级指针是存储变量的地址的,而一级指针变量在存储其他变量地址的时候,自己也在计算机中有对应的自己内存空间。既然把变量的地址放在指针变量里面叫做一级指针,那么我们把一级指针自身的地址存储到另外一个的指针变量中去,就把另外存储一级指针变量地址的指针叫做二级指针。
就是说二级指针是存储一级指针变量地址的指针变量。

  • 二级指针定义格式:
int a=50;
int* ptr1=&a;
int** ptr2=&ptr;//取出指针变量的地址

上面的 ** 号就表示ptr2是二级指针变量,存放的是一级指针ptr1变量的地址。

#include<stdio.h>
int main()
{
	int a = 10;
	int* ptr1 = &a;//此处*号表示ptr1是指针变量
	//*ptr1 = 50;//此处的*号是解引用操作
	int** ptr2 = &ptr1;
	**ptr2 = 50;
	printf("a=%d\n",a);
	return 0;
}
  • 一样可以改a的值:

在这里插入图片描述


🔑🔑数组指针

🎈🎈定义

在说之前,看一下这问题,数组指针变量是指针变量还是数组呢?
答案当然是:指针变量。

  • 我们在前面说到
  • int* ptr;//存整型变量的地址,能够指向整型数据的指针,叫做整型指针变量。
  • float* ptr1;//存放浮点类型的变量的地址,指向浮点类型数据的指针,叫做浮点型指针变量。
  • 那么数组指针,就是存放数组的地址,能够指向数组类型的指针变量了,叫做数组指针变量。

🎈🎈数组指针变量的格式

int (*ptr)[10];//ptr是指向数组的,数组的每个元素为整型。

我们来看,ptr 首先和 * 号结合,那么ptr就是指针变量。然后,在后面跟了 [10] 。说明该指针变量指向的是一个大小为10的整型数组。因此,ptr是指针变量,而指针指向的是数组,叫数组指针变量。

  • 注意点:‘[ ]’ 的优先级高于 ‘*’ 号的,因此必须加上 ‘( )’ ,保证ptr是指针变量。


🎈🎈数组指针初始化

在说到数组指针初始化的时候,不得不先说一个重要的知识点:

这有一个整形数组:int arr[10];//该数组可以放10个整形元素。
那么我们用到的下面两个有什么区别呢:

arr
表示数组首元素的地址,一个元素的地址用指针变量是这样的int* ptr1=arr;
arr==&arr[0];//它们的类型是 int*

&arr
表示取出的是整个数组的地址。
它们两个在数值上是一样的,但是意义不一样。
取出数组的地址,数组指针存储的是这样的 int (*ptr2)[10]=&arr;//这就是数组指针的初始化
ptr的类型是int (*)[10]//去掉名字,剩下的就是类型。
&arr的类型也是int (*)[10]

🎈🎈数组指针的应用

👉👉数组指针在二维数组中的应用

二维数组传参的本质:
首先,二维数组与一维数组很相似,其实可以这样理解,二维数组的每一行是一个一维数组,那么相当于二维数组的每个元素是一维数组,首元素就是第一行的一维数组。
因此,我们在传二维数组名的时候,就是传的二维数组第一行的地址。第一行为一维数组,所以形参要写成数组指针形式
比如:
int arr[3][4];//3行4列的二维数组。
arr:表述二维数组的首元素地址,相当于传的是第一行的地址,而第一行是一维数组,所以,在接收这个二维数组首元素地址的时候,形参就应该这样写:
int (*ptr)[4];//这就是数组指针,指向的是二维数组第一行的地址,一行有4个元素,每个元素的类型是int

接下来用两种方式把二维数组地址传给打印函数,不同的两种形参写法:
①二维数组形式

#include<stdio.h>
//传过来的是二维数组地址,我形参用二维数组接收,一一对应,好理解。
void print_array(int arr[3][5], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("arr[%d][%d]=%d  ",i,j,arr[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	//二维数组在初始化的时候,行可以省,列不能省略。
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	//用函数来打印二维数组
	print_array(arr,3,5);//把二维数组的地址传过去,行和列也要传过去
	return 0;
}

结果输出: 这里是引用

②数组指针形式:

#include<stdio.h>
void print_array(int(*ptr)[5], int r, int c)
{
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", *(*(ptr + i) + j));
			//ptr指向行,+i 找到第i行,解引用拿到第i行的地址名,相当于首元素地址,再+j找到对应的列的地址,最后解引用得到元素值。
		}
		printf("\n");
	}
}

int main()
{
	//二维数组在初始化的时候,行可以省,列不能省略。
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	//用函数来打印二维数组
	print_array(arr,3,5);//把二维数组的地址传过去,行和列也要传过去
	return 0;
}

结果打印:
在这里插入图片描述


🔑🔑函数指针

在了解函数指针的是什么之前,我们先来了解函数的地址:


🎈🎈函数的地址

  • 学了C语言基础知识了,我们知道各种数据类型,在创建变量的时候,内存会分配地址给这个变量,也就是说变量是有地址的;学到数组的时候,数组在内存空间中是连续存储的,也有地址。那么,我们想一想函数到底有没有地址呢?别慌,我们通过代码来验证一下猜想:👇👇👇👇
//2、函数地址
#include<stdio.h>
//写了一个加法函数
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10, b = 20;
	printf("%d+%d=%d\n\n",a,b,Add(a,b));
	
	//我把函数的地址取出来
	printf("%p\n", &Add);//用取地址符
	printf("%p\n", Add);//直接打印函数名
	return 0;
}
  • 结果如下:

在这里插入图片描述

从上面代码中可以看出,函数也是有地址的,并且函数名等价于&函数名,都是代表函数地址。

在这总结一下函数和数组的注意事项: ①首先函数是有地址的,并且 函数名 和 &函数名 ,拿到的都是函数的地址,两者没有任何差别。②但是,数组就不一样了, 比如:int arr[10]; arr 和 &arr 就有区别了。 &arr:是数组的地址,arr:是数组首元素地址,单从数值上看是一样的,但是意义完全不相同。

🎈🎈函数指针变量

我们通过类比看:
整型指针变量:是存放指针变量的地址。
字符指针变量:是存放字符变量的地址。
数组指针变量:是存放数组的地址。

那么,我们从上面知道,函数也是有地址的,要把函数地址存起来,那就要用到函数指针变量。

函数指针变量的定义

我们想啊,函数指针,见名知意。肯定得是指针,所以在写的时候,必须满足是指针,那么是指针,就用到 * 号,有 * 才表示是指针变量,如:(*ptr)。写成指针后,那么这个指针变量是指向函数的,而函数是用 ( )小括号括起来的,所以,这个指针变量也得用 (),如:(*ptr)( ) 。括号里里面的参数是根据原函数的参数多少和类型决定的。返回值也是看原函数的返回值,如:原函数是无参无返回值,那么函数指针就是这样:void (*ptr)( )

//函数指针变量
#include<stdio.h>
int Add(int x,int y)
{
    return x+y;
}

int main()
{
     printf("请输入两个整数:\n");
    int a=0,b=0;
    scanf("%d%d",&a,&b);
    //利用函数指针变量来调�?,先创建函数指�?
    int (*ptr)(int,int)=Add;//函数指针变量的格式,在参数里面x,y可以省略,只需要交代形参个数就可以。
    int num=(*ptr)(a,b);
    printf("结果为:%d\n",num);
    return 0;
}

在这里插入图片描述

  • 函数指针类型解释:
    在这里插入图片描述

✨✨指针数组

在这把指针数组拿来说,是因为在上面解释了数组指针,说数组指针是一个指针,且指向数组地址的指针变量。那么,对比来看,这指针数组又是什么呢?它是指针还是数组?
我们可以和之前的整型数组:是存放整型数据的数组吧,字符数组是存放字符的数组吧。那么好,指针数组就是存放指针的数组了,也就是数组里面放的都是地址。


👉👉指针数组模拟二维数组的应用

先用二维数组来存储打印:

//二维数组打印
#include<stdio.h>
int main()
{
    int  arr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};//3行5列的二维数组
    //打印
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<5;j++)
        {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }

    return 0;
}

在这里插入图片描述

用指针数组模拟二维数组

#include<stdio.h>
int main()
{
    //三个一维数组,用指针数组统一管理打印
    int arr1[5]={1,2,3,4,5};
    int arr2[5]={2,3,4,5,6};
    int arr3[5]={3,4,5,6,7};

    //首先创建指针数组
    int* ptr[]={arr1,arr2,arr3};
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<5;j++)
        {
            printf("%d ",ptr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

在这里插入图片描述

上面指指针数组模拟二维数组用下图来解释:
在这里插入图片描述


✨✨函数指针数组

函数指针数组?也是一样对比看,我们说指针数组是存放指针的数组,就是说这个数组里面都是地址,可以是整型类型的、字符型的。那么函数指针数组就是存放函数地址的数组,这里面存放了很多个函数的地址(有不同的函数名)。


👉👉函数指针数组在计算器(转移表)中的应用

最初代码展示:

//转移表(计算器)
#include<stdio.h>
void menu()
{
    printf("*****************************\n");
    printf("****  1、Add   2、Sub  ******\n");
    printf("****  3、Mul   4、Div  ******\n");
    printf("********** 0、eixt  *********\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 input=0;
    int x=0,y=0;
   do
   {
        menu();
        printf("请选择对应的计算:\n");
        scanf("%d",&input);
        switch(input)
        {
            case 1:
                //加法
                printf("请输入两个数数据:\n");
                scanf("%d%d",&x,&y);
                printf("%d+%d=%d\n",x,y,Add(x,y));
                break;
            case 2:
                //减法
                printf("请输入两个数数据:\n");
                scanf("%d%d",&x,&y);
                printf("%d-%d=%d\n",x,y,Sub(x,y));
                break;
            case 3:
                //乘法
                printf("请输入两个数数据:\n");
                scanf("%d%d",&x,&y);
                printf("%d*%d=%d\n",x,y,Mul(x,y));
                break;
            case 4:
                //除法
                printf("请输入两个数数据:\n");
                scanf("%d%d",&x,&y);
                printf("%d/%d=%d\n",x,y,Div(x,y));
                break;
            case 0:
                printf("退出计算器,欢迎下次使用!\n");
                break;
            default:
                printf("选择错误,请重新选择:\n");
                break;
        }
        
   } while (input);
    return 0;
}

结果展示:
在这里插入图片描述


代码优化:我从代码中看到,特别是在switch结构里面,代码有些类似,冗余度太高,如果继续增加运算,会添加很多相似的代码,代码灵活度太低。我们看到除了每个调用的函数不同,其他格式都差不多。这里我们就用一个函数指针数组来存放这几个函数的地址,通过函数指针变量来调用,会很方便。我们可以这样写,4个函数不变,只是在main函数里面变化:

int main()
{
    int input=0;
    int x=0,y=0;
    //函数指针数组的创建
    int (*ptr[5])(int,int)={NULL,Add,Sub,Mul,Div};
    //用NULL来占下标0的位置,这样就可以直接用input来使用下标,对应输入的功能值。
   do
   {
        menu();
        printf("请选择对应的计算:\n");
        scanf("%d",&input);

        if(input>=1&&input<=4)
        {
             //函数指针数组
            printf("请输入两个操作数:\n");
            scanf("%d%d",&x,&y);
            printf("%d\n",ptr[input](x,y));//调用
        }
       else  if(input==0)
        {
            printf("退出计算!\n");
            break;
        }
        else
        {
            printf("输入错误请重新输入:\n");
            break;
        }
   } while (input);
    return 0;
}

结果:
在这里插入图片描述

在这些,我们还可以改写一下代码,我们学了存储函数地址的指针,叫做函数指针,那么我们把调用部分封装成函数,在新的函数里面通过传过来的地址来调用:
代码如下:

函数指针变量的应用

  • 封装一个函数:
void Cal(int (*ptr)(int,int))
{
    int x=0,y=0;
    printf("请输入两个数数据:\n");
    scanf("%d%d",&x,&y);
    printf("%d\n", ptr(x,y));
}

那4个函数不变,在主函数里面直接调用就欧克:

int main()
{ 
    int input=0;
    int x=0,y=0;
 
    switch(input)
    {
      		case 1:
                //加法
                Cal(Add);
                break;
            case 2:
                //减法
                Cal(Sub);
                break;
            case 3:
                //乘法
                Cal(Mul);
                break;
            case 4:
                //除法
                Cal(Div);
                break;
            case 0:
                printf("退出计算器,欢迎下次使用!\n");
                break;
            default:
                printf("选择错误,请重新选择:\n");
                break;
        }
        
   } while (input);
    return 0;
}
  • 结果如下:
    在这里插入图片描述

各种指针就介绍到这,谢谢!🤞🤞

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值