第十一讲:指针(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;
}
相关推荐
~yY…s<#>1 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
EricWang13583 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
我是谁??3 小时前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
希言JY4 小时前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言
午言若4 小时前
C语言比较两个字符串是否相同
c语言
TeYiToKu6 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
互联网打工人no16 小时前
每日一题——第一百二十四题
c语言
爱吃生蚝的于勒6 小时前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法
羊小猪~~6 小时前
数据结构C语言描述2(图文结合)--有头单链表,无头单链表(两种方法),链表反转、有序链表构建、排序等操作,考研可看
c语言·数据结构·c++·考研·算法·链表·visual studio
洋2407 小时前
C语言常用标准库函数
c语言·开发语言