第十一讲:指针(3)

第十一讲:指针(3)

1.字符指针变量

1.1存储一个字符

我们当然可以使用指针来存放一个字符变量,使用方法如下:

1.2存储一个字符串

那么如果我们用指针存放字符串会发生什么呢?

结论:

1.指针存放字符串时存放的时一个字符串首元素的地址

2.直接使用指针存放字符串时,这个字符串被称为常量字符串,此时字符串的值不能被改变

c 复制代码
int main()
{
	char* pa = "abcdef";   //此时abcdef就是一个常量字符串
	                       //pa中存储的是首元素a的地址,证明如下:
	printf("%c\n", *pa);  //此时打印出的是a

	//*pa = 'c';  如果想要改变pa的值,程序会崩溃,但是并不会报错

	//所以我们不如将上面的代码表示如下
	const char* pb = "abcd";
	//*pb = 0;   此时如果再想改变pb的值,程序就会报错
	return 0;
}

1.3一个有趣的面试题

这个面试题的结果为:

原因如下:

str3和str4指向的是同一个常量字符串,常量字符串会存储再内存中的常量区,它的地址是确定的,而使用str1和str2初始化数组时,就会开辟不同的内存块

2.数组指针变量

2.1什么是数组指针变量

类比于

整形指针变量(int * pa),存放的时整形变量的地址,能够指向整形数据

浮点型指针变量(float * pb),存放的是浮点型变量的地址,指向浮点型数据
那么数组指针变量应该是:
存放的是数组的地址,能够指向数组的指针变量

指针变量的表示方法如下:

pa先和*进行结合,说明pa是一个指针变量,指向的是一个大小为5的数组。所以pa是一个指针,指向一个数组,称为数组指针

2.2数组指针变量的初始化

如果要存放个数组的地址,就得存放在数组指针变量中

数组指针解析:

3.二维数组传参的本质

结论:

二维数组传参,传入的是二维数组中首个一维数组的地址

我们用代码和图像来进一步理解二维数组传参的本质:

c 复制代码
//二维数组传参的本质 (打印二维数组的每一个元素)
Print(int(*pa) [3], int r, int c)  //二维数组传参的本质是传入了首个一维数组的地址,而首个一维数组的类型是int [3],
{                                   //所以接受的指针类型是int (*)[3]的类型
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%d ", (*(pa + i))[j]); //这里的(*(pa + i))[j]相当于*(*(pa+i)+j)相当于pa[i][j]
		}
		printf("\n");
	}
}

int main()
{
	int arr[2][3] = { {1,2,3}, {2,3,4} };
	Print(arr, 2, 3);
	return 0;
}


当然,二维数组进行传参时,也可以用二维数组来接受:

c 复制代码
Print(int arr[2][3], int r, int c)

4.函数指针变量

4.1介绍函数指针变量

类比于数组指针变量(用来存放数组的地址),函数指针变量是用来存放函数的地址的,对于函数的地址的理解:

我们可以看出来,函数名其实就是函数的地址,Add == &Add,为了将函数的地址存下来,就要使用函数指针变量,使用方法如下:

c 复制代码
//函数指针的使用
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 2;
	int b = 3;
	         //(int x, int y)中x和y写不写都可以
	int (*Pa)(int, int) = Add; //将Add函数的地址存储在函数指针变量中,Add和&Add等价
	printf("%d\n", Pa(a, b));  //在使用时,pa(a,b)和(*pa)(a,b)等价
	return 0;
}

函数指针类型解析:

4.2 两段有趣的代码

4.2.1代码1

c 复制代码
(*(void (*)())0)();

对于上述代码的解释如下:

4.2.2代码2

c 复制代码
void (*signal(int , void(*)(int)))(int);

4.3typedef关键字

typedef是用来类型重命名的,可以将复杂的类型简单化,具体使用方法如下:

c 复制代码
//typedef关键字
typedef unsigned int uint;    //1.使用关键字定义普通类型的名称
typedef int* pa;              //2.使用关键字定义指针类型的名称
typedef int(*ppa)[4];         //3.使用关键字定义数组指针的名称
typedef int(*pppa)(int, int); //4.使用关键字定义函数指针变量名称

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

int main()
{
	uint a = 3;  //关键字的使用
	printf("%d\n", a);

	pa p_a = &a;
	*p_a = 4;
	printf("%d\n", a);
	
	int arr[4] = { 1,2,3,4 };
	ppa p_arr = &arr;
	for (int i = 0; i < 4; i++)
	{
		printf("%d ", *((*p_arr) + i));
	}
	printf("\n");

	pppa p_add = Add;
	int ret = p_add(2, 3);
	printf("%d", ret);
	return 0;
}

那么此时我们就可以简化代码2,将其转换成:

c 复制代码
void (*signal(int , void(*)(int)))(int);

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

5.函数指针数组

函数指针数组是存放函数指针的数组,那么函数指针数组如何使用呢?

c 复制代码
//函数指针数组的使用
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*pa[3])(int, int) = { Add };
	int ret = pa[0](2, 3);
	printf("%d", ret);
	return 0;
}

6.转移表

函数指针的用途:转移表

举例:计算器的一般实现

6.1一般写法

c 复制代码
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;
}

显然,这个方法比较啰嗦,不好用

6.2函数指针数组写法

c 复制代码
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 (*pa[5])(int, int) = { 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 >= 1 && input <= 5)
		{
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			int ret = pa[input](x, y);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出程序!");
			break;
		}
		else
			printf("输入的值非法,重新输入:");
		
	} while (input);
	return 0;
}
相关推荐
Ylucius30 分钟前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
.普通人2 小时前
c语言--力扣简单题目(回文链表)讲解
c语言·leetcode·链表
星迹日2 小时前
C语言:联合和枚举
c语言·开发语言·经验分享·笔记
Huazzi.2 小时前
算法题解:斐波那契数列(C语言)
c语言·开发语言·算法
DdddJMs__1352 小时前
C语言 | Leetcode C语言题解之题409题最长回文串
c语言·leetcode·题解
元气代码鼠2 小时前
C语言程序设计(进阶)
c语言·开发语言·算法
做完作业了3 小时前
【C语言】预处理详解
c语言·预处理
aqymnkstkw4 小时前
2024年【电气试验】考试题库及电气试验模拟试题
大数据·c语言·人工智能·嵌入式硬件·安全
Happy鱿鱼7 小时前
C语言-数据结构 有向图拓扑排序TopologicalSort(邻接表存储)
c语言·开发语言·数据结构
KBDYD10107 小时前
C语言--结构体变量和数组的定义、初始化、赋值
c语言·开发语言·数据结构·算法