深入理解指针4


(一)字符指针变量

字符指针的一般使用,和之前指针变量存储整型变量使用方法一样

int main()

{

	char ch = 'a';
	char* pa = &ch;
	//*pa='a';
	return 0;
}

还有一种使用方法,如下所示

int main()

{
	char ch = "abcdef";
	const char* pa = "abcdef";
	return 0;
}

这里并不是把字符串abcdef存入到字符指针pa中,而是将字符串首字母的地址存放pa中 这里用const*pa来存储常量字符串的首元素地址,const表示不希望修改字符串的内容

观察以下代码,更深入的了解字符数组和字符指针的区别

int main()

{
	char s1[] = "abcdef";
	char s2 [] = "abcdef";
	const char* s3 = "abcdef";
	const char* s4 = "abcdef";
	if (s1 == s2)
	{
		printf("s1==s2\n");
	}
	else
	{
		printf("s1!=s2\n");
	}
	if (s3 == s4)
	{
		printf("s3==s4\n");

	}
	else
	{
		printf("s3!=s4\n");

	}

	return 0;
}

这里的s1和s2虽然用相同的常量字符串去初始化它们,但是它们会开辟出不同的内存块去存储,但是s3和s4作为字符指针,它们指向同一个字符串的时候,实际上会指向同一个内存,所以s1和s2不同,s3和s4相同

(二)数组指针变量


1.数组指针变量的定义

数组指针是指针变量,里面存放的是数组的地址,是能够指向数组的指针变量,一般定义为如下所示

int main()

{
	char arr[6];
    char *pc[6]=&arr;    //指针数组
	char (*pa)[6] = &arr;//数组指针
	return 0;
}

注意⚠️:*pa要用()包含,因为[]的优先级高于*,如果不用()将*pa包含在内,那么就会变成指针数组,表示存放数组的指针


2.数组指针变量的初始化
int main()

{
	int arr[10] = { 0 };
	int (*p3)[10] = &arr;//int(*)[10] 数组指针类型
	return 0;
}

之前我们用指针数组将数组中每个元素都访问了一遍,并且能够正确的在屏幕上打印出来,那么我们用数组指针也能访问数组的每个元素呢,答案是可以的,只不过和指针数组不同,在打印数组中的每个元素时不能使用p++,因为这里的p+1代表的不是跳过一个字节,而是跳过一整个数组的字节,所以我们可以使用(*p)[i]来逐个打印,这里的*p代表*&arr,也就是代表arr

int main()

{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*p)[10] = &arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ",(*p)[i]);
	}


	return 0;
}

那么可能会有人想,这么做要比之前用指针数组做的时候要麻烦,那是因为数组指针并不是用在这个地方的,而是接下来的二维数组传参中的

3.二维数组传参的本质

我们之前学习了一维数组传参的本质,它是讲数组首元素的地址传递给函数,而二维数组其实也是将二维数组首元素的地址传递给函数形参部分,只不过二维数组中的元素是一维数组,所以二维数组首元素的地址就是一维数组那一行的地址,而这时候我们可以用数组指针接受第一行数组的地址,接着总之前所学的知识将二维数组的每个元素打印出来

void print(int(*arr)[5], int i, int j)
{
	int a = 0;
	int b = 0;
	for (a = 0; a < i; a++)
	{
		for (b = 0; b < j; b++)
		{
			printf("%d ", *(*(arr + a) + b));
		}
		printf("\n");
	}

}
int main()

{
	int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	print(arr, 3, 5);

	return 0;
}

注意⚠️:在函数部分用数组指针接受的首行数组元素的地址,要用int(*p)[5],表示指向一行5个int,而不是用int(*p)[3][5],int(*p)[3][5]表示的是指向整个3×5二维数组的指针,而这里我们只需接受首行5个int类型的变量的地址

这里的*(arr+a)表示的是第几行,a=0时,表示二维数组的第一行,以此类推,然后在后面加上b表示从第0列开始往后依次访问,最终表达式就为*(*(arr+a)+b)

(三)函数指针变量


1.函数指针变量的创建

函数指针变量就是存放函数的地址,那么函数有没有地址呢,我们可以试一下

观察发现,函数也是有地址的,所以我们用函数指针变量来存放函数的地址 函数指针变量的创建格式一般如下

int Add(int x, int y)
{
	return x + y;
}
int add(int a, char ch)
{

}
int main()

{
	int(*pf)(int,int) = &Add;//pf就是函数指针变量
	int(*pa)(int, char) = &add;//pa也是函数指针变量
	return 0;
}

2.函数指针变量的使用

 我们可以通过解引用操作符*来获取函数指针变量所指向的函数,在这里注意一点,函数名也是函数的地址,所以&Add和Add其实是一样的

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

int main()

{
	int(*pf)(int,int) = &Add;//pf就是函数指针变量
	int re = (*pf)(3, 5);
	printf("%d", re);
	return 0;
}

这里的(*pf)指向了Add函数,其实pf=&Add=Add,所以这里不用解引用操作符*,直接用pf也可以直接指向函数Add()

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

int main()

{
	int(*pf)(int,int) = &Add;//pf就是函数指针变量
	int re = pf(3, 5);//pf=&Add=Add
	printf("%d", re);
	return 0;
}

依旧能得出结果


3.typedef
(1)普通变量类型

typedef是用来类型重命名的,比如说我们定义一个unsigned int类型的变量,我们觉得这个单词太长了,以后写起来不方便,我们就可以用typedef来重新命名,如下所示

int main()
{
	typedef unsigned int uint;
	unsigned int b;
	uint a;
	
	return 0;
}

调试我们发现a,b变量均为unsigned int类型

(2)指针类型

我们可以用typedef重新命名指针变量,如下所示

int main()
{

	typedef int* pf;
	int* pa;
	pf pb;

	return 0;
}

我们可以看出pa和pb的类型都是int*类型

typedef还有一个方便的地方,比如说我们想要定义多个int*类型的变量,使用使用如下代码,

int main()
{
	int* pa, pb, pc;
	
	return 0;
}

但是调试可以发现,只有第一个变量是int*类型,其他的是int类型,这时候我们就可以使用typedef来重命名int*,如下所示

int main()
{
	typedef int* pf;
	pf pa, pb, pc;
	
	return 0;
}

调试我们发现所有变量均为int*类型

(3)数组指针和函数指针

它们和上面两个的命名有点不同,它们的命名格式如下

int main()
{
	typedef int(*pf)[5];//数组指针类型
	int(*arr1)[5];
	pf arr2;//pf=int (*)[5]

	int (*pa)();
	typedef int (*pc)();//函数指针类型
	pc pv;//pc=int (*)()

	return 0;
}


(四)函数指针数组

我们把函数的地址存放到一个数组中去叫作函数指针数组,定义如下


int main()

{

	int (*parr[5])();//parr先和[]结合,说明parr是数组,
                     //而数组的内容是int(*)()类型的函数指针
	return 0;
}

那么具体该如何去使用它呢,我们通过下面一个题目深入了解


1.转移表

计算机的实现

我们完成一个简单计算机的代码实现,要求用户输入两个数字,可以进行简单的加减乘除运算,我们先不用函数指针数组来完成,通过之前的扫雷和猜数字游戏,相信我们可以写出这段代码,如下所示

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>


int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int mlt(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}

void menu()
{
	printf(".....................\n");
	printf("......1.Add.........\n");
	printf("......2.Sub.........\n");
	printf("......3.mlt..........\n");
	printf("......4.div..........\n");
	printf("......0.over..........\n");


}

int main()

{

	int a = 0;
	int b = 0;
	int input = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &a, &b);
			ret = Add(a,b);
			printf("%d", ret);
			break;

		case 2:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &a, &b);
			ret = Sub(a,b);
			printf("%d", ret);
			break;

		case 3:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &a, &b);
			ret = mlt(a,b);
			printf("%d", ret);
			break;

		case 4:
			printf("请输入两个操作数:\n");
			scanf("%d %d", &a, &b);
			ret = div(a,b);
			printf("%d", ret);
			break;

		case 0:
			printf("退出游戏\n");
			break;

		default:
			printf("输入错误\n");
			break;

		}

	} while (input);



	return 0;
}

我们可以发现,在case语句中,有太多冗长的相同的语句,为了使代码更加简洁,我们可以使用函数指针数组,将4个运算函数的地址存入其中

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>


int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int mlt(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}

void menu()
{
	printf(".....................\n");
	printf("......1.Add.........\n");
	printf("......2.Sub.........\n");
	printf("......3.mlt..........\n");
	printf("......4.div..........\n");
	printf("......0.over..........\n");

}

int main()

{

	int a = 0;
	int b = 0;
	int input = 0;
	int ret = 0;
	int(*parr[5])(int, int) = { 0,Add,Sub,mlt,div };
	do                      //数组首元素的下标是0,所以加一个数字,
	{                       //这样加减乘除的函数下标就分别是1,2,3,4
		menu();
		printf("请选择:\n");
		scanf("%d", &input);
		if (input <= 4 && input >= 1)
		{
			printf("请输入两个操作数:");
			scanf("%d %d", &a, &b);
			ret = (*parr[input])(a, b);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出游戏\n");

		}
		else
		{

			printf("输入错误\n");

		}

	} while (input);



	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值