”数组指针变量与函数指针变量“宝典

大家好呀,我又来啦!最近我很高效对不对,嘿嘿,被我自己厉害到了。

这一节的内容还是关于指针的,比上一期稍微有点难,加油!!!

点赞收藏加关注,追番永远不迷路。

在这里插入图片描述

1. 字符指针变量

根据之前学习的内容理解,字符指针变量就是:指针变量指向的对象是字符类型。

	char ch = 'w';
	char* pc = &ch;

这期我们学习的是char* pc = "abcdef";这种类型 。我们先从简单的开始理解

char arr[] = "qwerty"; //数组是一块连续的空间,qwerty它们是连续存放的
char* pc = arr; //这里的arr是数组首元素(q)的地址
printf("%c", *pc); //所以打印出来的是q

在此,我们先创建了字符数组【数组里的字符是可以修改的】 ,然后将q的地址储存在指针变量pc中,最后打印出来。(刚开始在这里我想错了,我想将数组的所有的地址传给指针变量,再解引用就可以打印出来数组的整个元素,提醒大家,指针变量的大小只能放4个字节,不可能放下所有地址)

char* pc = "qwerty"; //这里的qwerty是常量字符串
printf("%c", *pc);

与上面一样,这个字符串也是放在连续的空间【常量字符串是不可以修改的,但是你修改的话,并不会报错,所以我们可以这样写const char* pc = “qwerty”,这样如果我们后面忘记它不可以修改,不小心修改了,它会给我们报错的】

char* pc = "qwerty"; 这里的的赋值不是将qwerty放在p里(毕竟全部放进去需要6个字节的空间,p的空间是4个字节,大小也不够),而是将字符串中首字符的地址放在里面。

总结:常量字符串作为表达式的时候,赋值给别人,是将首字符的地址赋给别人的。接下来打印出来印证一下。

在这里插入图片描述

那如何打印整个字符串呢?使用%s打印字符串的时候,只需要提供字符串的地址

在这里插入图片描述

  • 还有一个需要注意的是:当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。

2. 数组指针变量

在上一期我们了解了指针数组(本质是数组,数组中存放的是地址)。这一节我们学习数组指针,从名字可以知道,它的本质是指针。那数组指针变量是用来做什么的?是用来存放数组的地址

我们从之前学的知识类比过来

  • 字符指针:char*(字符指针变量中存放的是字符的地址–char*指向字符的指针)
char ch = 'q';
char* pc = &ch;*和pc,*能说明pc是指针,char说明pc指向的变量是char类型)
  • 整型指针:int*(整型指针变量中存放的是整型的地址–int*是指向整型的指针)
int a = 9;
int* pa = &a;*和pa,*能说明pa是指针,int说明pa指向的变量是int类型)
  • 数组指针:(数组指针变量中存放的是数组的地址(&数组名)指向数组的指针)

如果要存放个数组的地址,就得存放在数组指针变量中
在这里插入图片描述

int arr[10] = { 0 };
int(*p)[10] = &arr;
//p的旁边放*,说明p是指针,指向的数组

这里说明一下为什么指向的是数组:在这一章节链接:C语言之脆皮牌数组宝典我们说过数组的类型是什么,这里数组int arr [10]的类型是int [10]。将(*p)去掉,剩下int [10],所以p指向的对象是数组。

arr(数组首元素地址)----int*----arr+1:跳过4个字节
&arr[0](数组首元素地址)----int* ---- &arr[0]+1 : 跳过4个字节
&arr(数组的地址)----int (*) [9]----&arr+1:跳过整个数组(4*sz个字节)

3. 二维数组传参的本质

以前我们有一个二维数组的需要传参给一个函数的时候是这样的:实参是二维数组,形参也写成二维数组的形式。

int print(int arr[3][5], int h, int l)
{
	int i, j = 0;
	for (i = 0; i < h; i++)
	{
		for (j = 0; j < l; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5} };
	print(arr, 3, 5);
}

还有什么写法呢?

我们先理解一下二维数组,二维数组其实可以看成一维数组的数组(将一维数组看作是一个元素),数组名是数组首元素的地址,这里的首元素是一行数组,在此之前我们已经知道:数组的地址放在数组指针变量里。那形参写什么嘞?第一行的一维数组的类型是int [5] ,所以第一行的地址的类型是int(*)[5],所以形参是int ( * arr)[5]。

那就意味着**二维数组传参本质上也是传递了地址**,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的。

  • 写代码时需要理解的内容:

在这里插入图片描述

(1)从上图可以看出,若想拿到第i行的内容,可以写 *(arr+i),且这个和arr[i]完全等价。

在这里插入图片描述

(2) 从上图可以看出,arr[i]是第 i 行的数组名,数组名又是首元素的地址,所以arr[i]就是第i行第一个元素的地址,即arr[ i ] 就是 &arr[ i ][ 0 ], * (arr+i) 就是&arr [ i ][ 0 ] 。当写下 * (arr+i),它的意思就是第i行首元素地址。接下来出现 j 的是列 , * (arr+i)+j 的意思是第 i 行第 j 列的元素的地址,再将它解引用就是第 i 行第 j 列的元素: * (* (arr+i)+j )

  • 最终写出来的代码是:
int print(int (*arr)[5], int h, int l)
{
	int i, j = 0;
	for (i = 0; i < h; i++)
	{
		for (j = 0; j < l; j++)
		{
			printf("%d ", *( * (arr + i) + j));
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5} };
	print(arr, 3, 5);
	//arr是第一行数组的地址
}

4. 函数指针变量

函数也有自己的地址

  1. 以最简单的加法为例,看一下函数地址
int add(int m, int n)
{
	return m + n;
}
int main()
{
	int x = 90;
	int y = 89;
	int r = add(x,y);
	printf("%p\n", add);
	printf("%p\n", &add);
	//函数名和&函数名都是函数的地址,没有区别。有无&均可,它们的含义是一样的
}
  1. 整型变量的地址放在整形指针变量,字符变量的地址放在字符指针变量,所以,函数的地址放在函数指针变量中。(首先是指针,(*pf)。接着它是指向函数,(*pf)(函数的参数)。最后写它的返回值类型,返回类型(*pf)(函数的参数))下面的例子仍然以前面那个加法函数为例
int (*pf)(int,int) = add;  //形参加不加都可以
int (*pf)(int x,int y)= add; 
  1. 如何通过函数地址调用函数呢?先解引用函数指针变量,再将参数传过去即可。
int (*pf)(int,int) = add;
int r = (*pf)(3, 9); //有无*均可,有*好理解
int r1 = pf(3,9);
//正常通过函数名的方式也可以调用它
//int r = add(3,9);
printf("%d\n", r);

在这里插入图片描述

5. typedef关键字

typedef 是用来类型重命名的,可以将复杂的类型,简单化。

  1. 以无符号整型变量为例
typedef unsigned int uint;
int main()
{
	//这两种方式是一样的
	unsigned int a = 99;
	uint a = 99;
}
  1. 以整型指针变量为例
typedef int* zhiint;
int main()
{
	int a = 9;
	zhiint pa = &a;
}
  1. 以数组指针变量为例(有略微不同,将新名字放在*旁边)
typedef int(*shuzu)[5];
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	shuzu parr= &arr;//原本是int (*parr)[5]=&arr;
}
  1. 以函数指针变量为例(有略微不同,将新名字放在*旁边)
int add(int m, int n)
{
	return m + n;
}
int (*pff)(int, int);
int main()
{
	     pff = add; 
//原本是:int (*pf)(int, int) = add;
}

6. 函数指针数组

创建一个数组,它的每个元素都是函数的地址。(注意注意:函数的返回值类型,参数个数一定要是相同的才可以)

在此之前,我们只能一个一个的创建函数指针变量:

//函数的声明
int add(int x, int y);
int sub(int x, int y):
int mul(int x, int y);
int div(int x, int y):
int main()
{
	int (*pa)(int, int) = add;
	int (*pb)(int, int) = sub;
	int (*pc)(int, int) = mul;
	int (*pd)(int, int) = div;
}
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 (*pf[4])(int, int) = { add,sub,mul,div };
//在这里先说明一下: 

//如果是int(*pf[4]) (int,int):说明pf先和[4]结合,使得pf是一个数组。
//将数组去掉,剩下int ( * )(int,int),这是函数指针。

//如果是(*pf)[4],则pf先和*结合,使得pf是指针,指向数组[ 4 ]

在函数指针数组里,通过下标0可以找到add函数的地址,以此类推。

int (*pf[4])(int, int) = { add,sub,mul,div };
                          //0    1  2   3
int i = 0;
for (i = 0; i < 4; i++)
{
	int r = pf[i](9, 3);
	//pf[i]是数组中下标为i的元素,(9,3)是将参数传过去
	printf("%d\n", r);
}

7. 转移表

那函数指针数组可以如何用呢?给大家在计算器里演示一下。

以前我们这样写,很繁琐:

#include <stdio.h>
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;
    do
    {
        printf("*************************\n");
        printf("  1:add           2:sub  \n");
        printf("  3:mul           4:div  \n");
        printf("  0:exit                 \n");
        printf("*************************\n");
        printf(" 请选择: ");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("输⼊操作数:");
            scanf("%d %d", &x, &y);
            ret = add(x, y);
            printf("ret = %d\n", ret);
            break;
        case 2:
            printf(" 输⼊操作数:");
            scanf("%d %d", &x, &y);
            ret = sub(x, y);
            printf("ret = %d\n", ret);
            break;
        case 3:
            printf("输⼊操作数:");
            scanf("%d %d", &x, &y);
            ret = mul(x, y);
            printf("ret = %d\n", ret);
            break;
        case 4:
            printf("输⼊操作数: ");
            scanf("%d %d", &x, &y);
            ret = div(x, y);
            printf("ret = %d\n", ret);
            break;
        case 0:
            printf("退出程序\n");
            break;
        default:
            printf("选择错误\n");
            break;
        }
    } while (input);
    return 0;
}

现在我们可以利用函数指针数组来实现:

#include <stdio.h>
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;
    int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表

    do
    {
        printf("*************************\n");
        printf("  1:add           2:sub  \n");
        printf("  3:mul           4:div  \n");
        printf("  0:exit                 \n");
        printf("*************************\n");
        printf(" 请选择: ");
        scanf("%d", &input);
        if ((input <= 4 && input >= 1))
        {
            printf("输⼊操作数:" );
            scanf("%d %d", &x, &y);
            ret = (*p[input])(x, y);
            printf("ret = %d\n", ret);
        }
        else if (input == 0)
        {
            printf("退出计算器\n");
        }
        else
        {
            printf("输⼊有误\n" );        
        }
    } while (input);
    return 0;
}

到此结束,感谢大家观看,码字不易,留个小心心叭!!!蟹蟹。

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值