深入理解指针(4)

目录

字符指针变量

数组指针变量

二维数组传参的本质

函数指针变量

函数指针变量的创建

函数指针变量的使用

两个有趣的代码

typedef 关键字

函数指针数组

转移表


字符指针变量

字符指针变量是指向字符的,储存的是字符的地址。

int main()
{
	char ch = 'w';
	char* p = &ch;
	printf("%c ", *p);
}

int main()
{
	char arr[10] = "abcdefg";
	char* p = arr;
	printf("%s ", p);
	printf("%s ", arr);
	return 0;
}

#include<stdio.h>
int main()
{
	const char* p = "abcdef";//字符串"abcdef"为常量字符串,不能被修改
	printf("%c ", *p);//这次赋值是将字符串首字符a的地址赋给p
	//*p='w';
	//不被允许,原因是"abcdef"为常量字符串不能修改,否则程序会崩溃。
	return 0;
}

数组指针变量

指向数组的指针,存放的是数组的地址。

要跟   指针数组  做好区分。

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int (*p)[10] = &arr;
	//p的类型,去掉p,即int (*)[10]
}

既然有数组指针变量,那是否能利用数组指针变量来打印数组内容呢?

答案是肯定的。

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int (*p)[10] = &arr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", (*p)[i]);
		//*p=arr
	}
	return 0;
}

但是这样用大家是不是会觉得太麻烦?远远不如用数组来得简单?

这是因为很少人会这样用数组指针类型 ,那更为常见的方法是什么呢?

这就要谈谈二维数组传参的本质了。

二维数组传参的本质

我们先来用函数打印一下二维数组。

#include<stdio.h>
void Printf(int arr[4][6],int r,int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%2d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[4][6] = { {1,2,3,4,5,6},{7,8,9,10,11,12},{13,14,15,16,17,18},{19,20,21,22,23,24} };
	//打印数组元素
	Printf(arr,4,6);
}

这里实参是二维数组,形参也写成二维数组形式,还有其他写法吗? 

二维数组的每一行其实是一个一维数组,那么二维数组其实可以理解为是一维数组的数组。

也就是说,二维数组的每个元素其实是一个一维数组。

 按照这样的逻辑,二维数组的数组名代表的就是第一行的地址,是一维数组的地址。

那就意味着,二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。

那么形参也是可以写成指针形式的:

void Printf(int(*arr)[6], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%2d ", *(*(arr+i)+j));//用arr[i][j]也一样。
		}
		printf("\n");
	}
}
int main()
{
	int arr[4][6] = { {1,2,3,4,5,6},{7,8,9,10,11,12},{13,14,15,16,17,18},{19,20,21,22,23,24} };
	//打印数组元素
	Printf(arr,4,6);
}

 

函数指针变量

函数指针变量的创建

函数指针是指向函数的,储存的是函数的地址。

函数名是函数的地址,&函数名也是函数的地址。

注意:

在数组中,arr!=&arr.

而在函数中,例:add == &add.

#include<stdio.h>
int add(int a, int b)
{
	return a + b;
}
int main()
{
	int a = 1;
	int b = 4;
	int r = add(a, b);
	printf("%p\n", add);
	printf("%p", &add);
	return 0;
}

如果我们要把函数的地址存放起来,当然就需要函数指针变量 。

int main()
{
	int (*p)(int a, int b) = &add;
	int (*p)(int , int ) = &add;
	return 0;
}

上面的代码,变量省不省略都是可以的 。

函数指针类型就是:int(*)(int ,int)

函数指针类型解析:

int   :代表指针指向函数的返回类型

*  :代表指针。

p :函数指针变量名。

(int ,int)  :指针指向函数的参数类型和个数的。

函数指针变量的使用

通过函数指针调用指针指向的函数

#include<stdio.h>
int add(int a, int b)
{
	return a + b;
}
int main()
{
	int(*p)(int, int) = &add;
	int r = p(3, 4);//p=&add=add
	printf("%d ", r);
	return 0;
}

ge用*p/p都是可以的。

两个有趣的代码

大家可以思考一下它代表的含义,再看我给的解释哦~

(*(void(*)())0)();

这个代码看着是不是很让人头疼?让我们来找一个切入点吧。

从0开始看,0前面的()中是一个函数指针类型,例如:int r=(int)3.14。你是否可以猜出来这是强制类型转换呢?然后把这个指针解引用再使用这个函数。这就是这段代码的意义了。

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

切入点 signal(int,void(*)(int))从这里我们可以看出来它是一个函数,一个参数类型为int,另一个为void(*)(int)。然后看剩余的一部分,void(*)(int)你是否可以看出它是一个函数指针类型呢? 

结论:这串代码是一个函数声明!!!你看出来了吗?

其实 void(*signal(int,void(*)(int)))(int)==void(*)(int)  signal(int,void(*)(int)),为了体现它指针的性质,我们把它放在*附近。

typedef 关键字

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

如果我们觉得 unsigned int不方便,我们可以把它重命名为 unin

typedef unsigned int unin;
unin a = 10;

同样,我们也可以命名指针类型

typedef int* nin;

但是,函数指针跟数组指针稍微有点区别。

typedef int(*as)[6];
typedef int(*ds)(int, int);

将 int(*)[6] 类型重命名为 as。

最后,我再告诉大家一个小细节,

int*p1,p2;类型是否相同呢?

答案是否定的,p1的类型为 int*,p2则为int。正确的写法是:int*p1,*p2;

而用typedef重命名的类型则不一样,nin p3,p4; 类型都为int*。 

函数指针数组

前边我们已经学习了指针数组,我们把函数的地址放在数组中,就叫函数指针数组。

函数指针数组的创建:

int(*arr[6])(int, int);

转移表

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

使用函数来实现计算机,大家会不会感觉这有点过于繁琐,冗余呢?

void menu()
{
	printf("***********************\n");
	printf("***1.add*****2.sub*****\n");
	printf("***3.mul*****4.div*****\n");
	printf("********0.exit*********\n");
	printf("***********************\n");

}
void add(int x, int y)
{
	printf("%d\n", x + y);
}
void sub(int x, int y)
{
	printf("%d\n", x - y);
}
void mul(int x, int y)
{
	printf("%d\n", x * y);
}
void div(int x, int y)
{
	printf("%d\n", x / y);
}
int main()
{
	int a = 0;
	int x = 0,y=0;
	do 
	{
		menu();
		printf("请选择:");
		scanf("%d", &a);
		switch (a)
		{
		case 1:
			printf("您已进入游戏\n");
			scanf("%d %d", &x, &y);
			add(x,y);
			break;
		case 2:
			printf("您已进入游戏\n");
			scanf("%d %d", &x, &y);
			sub(x,y);
			break;
		case 3:
			printf("您已进入游戏\n");
			scanf("%d %d", &x, &y);
			mul(x,y);
			break;
		case 4:
			printf("您已进入游戏\n");
			scanf("%d %d", &x, &y);
			div(x,y);
			break;
		case 0:
			printf("您已退出游戏\n");
			break;
		default:
			break;
		}

	} while (a);
	return 0;
}

接下来让我们看看用函数指针数组代码的实现吧。

#include<stdio.h>
void menu()
{
	printf("***********************\n");
	printf("***1.add*****2.sub*****\n");
	printf("***3.mul*****4.div*****\n");
	printf("********0.exit*********\n");
	printf("***********************\n");

}
void add(int x, int y)
{
	printf("%d\n", x + y);
}
void sub(int x, int y)
{
	printf("%d\n", x - y);
}
void mul(int x, int y)
{
	printf("%d\n", x * y);
}
void div(int x, int y)
{
	printf("%d\n", x / y);
}


int main()
{
	int a = 0;
	void(*arr[5])(int, int) = { 0, add,sub,mul,div };//多加一个0,使得case 数值后的数字与选择的函数相匹配。

	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &a);
		if ((a > 0) && (a < 5))
		{
			int x = 0, y = 0;
			printf("您已进入游戏\n");
			scanf("%d %d", &x, &y);
			arr[a](x, y);
		}
		else if (a == 0)
			printf("退出");
		else
			printf("你输错了");
		
		
	} while (a);
	return 0;
}

今天的内容到此结束,下一个博客再见~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值