2024年最新view的绘制流程面试,C语言篇+ 指针进阶(上),2024年最新程序员面试八股文

收集整理了一份《2024年最新Python全套学习资料》免费送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img



既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Python知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来

如果你需要这些资料,可以添加V无偿获取:hxbc188 (备注666)
img

正文

(*arr)[i],数组指针是指向数组的地址,对指针解引用就能找到指针指向的内容,*arr表示的是数组的首元素地址,有了首元素的地址就可以向后偏移i位,其实等价于arr[i]

*(*arr + i),数组指针是指向数组的地址,对指针解引用就能找到指针指向的内容,*arr表示的是数组的首元素地址,对首元素的地址(数组首地址),偏移i个长度,会跳过4 * i个字节,再将偏移后的指针解引用找到指针指向的内容

希望以上的解释对大家的理解有所帮助,这也是博主对这些知识在一定程度上的理解,对这些语法的解释

接下来再看看另外一种用法


/\* 常规写法 \*/
void func(int arr[3][5],int row,int col) 
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
}

/\* 使用数组指针 \*/
void func(int (\*arr)[5],int row,int col) 
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
		/\* 以下两种方式都可以 \*/
			printf("%d ",arr[i][j]);
			printf("%d ", \*((\*arr + i) + j));
		}
		printf("\n");
	}
}

int main() 
{
	int arr[3][5] = {1,2,3,4,5,2,3,4,5,6,3,4,5,6,7};
	int row = sizeof(arr) / sizeof(arr[0]);
	int col = sizeof(arr[0]) / sizeof(arr[0][0]);
	func(&arr,row,col);
	return 0;
}

主要还是说一下数组指针的使用方式

首先我们来看这是35的二维数组
在这里插入图片描述
当使用数组指针的时候获取的是它的下标为【0】的一维数组的地址,数组指针里面存放的就是它的地址
在这里插入图片描述
假设现在要找处arr[1][4]的元素,下面我们直接看图
在这里插入图片描述
在这里是通过让数组指针指向下标【1】的位置处,有了数组的起始地址向后偏移,方便找到其他的元素,不过这种int(
)[5]类型的数组指针只是针对一维数组的,存放的是一维数组的地址,这一点请大家务必注意

有了上面知识的理解,再来看一组,你现在知道它们代表着什么吗?
在这里插入图片描述

1、int arr[5]; --》 表示的是一个数组,数组的每一个元素都是一个int
2、int *parr1[10]; --》表示的是指针数组,数组中的每个元素都是int *类型的指针变量
3、 int (*parr2)[10]; --》数组指针,本质上是一个指针,指向的是一个数组,数组的元素个数是10,数组的元素类型是int
4、int (*parr3[10])[5]; 重点来了,先看操作符的优先级,()的优先级是最高的,所以先看()里的内容,其次是[ ]优先级是第二,arr3先和[ ] 结合就是一个数组,数组里面存放着10个元素,每一个元素的类型是 int( * )[5],所以每一个元素都是一个数组指针,该数组指针指向的数组是【5】个元素,每一个元素是int类型,在这里插入图片描述

四、数组参数、指针参数

1.1一维数组传参

#include <stdio.h>
void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int \*arr)//ok
{}
void test2(int \*arr[20])//ok
{}
void test2(int \*\*arr)//ok
{}
int main()
{
 int arr[10] = {0};
 int \*arr2[20] = {0};
 test(arr);
 test2(arr2);
}

1.2二维数组传参

void test(int arr[3][5])//ok
{}
void test(int arr[][])//err
{}
void test(int arr[][5])//ok
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int \*arr)//err
{}
void test(int\* arr[5])//err
{}
void test(int (\*arr)[5])//ok
{}
void test(int \*\*arr)//err
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

1.3一级指针传参

#include <stdio.h>
void print(int \*p, int sz) {
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", \*(p+i));
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int \*p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0; }

1.4二级指针传参

#include <stdio.h>
void test(int\*\* ptr) {
 printf("num = %d\n", \*\*ptr); 
}
int main()
{
 int n = 10;
 int\*p = &n;
 int \*\*pp = &p;
 int \*arr[10];//指针数组
 test(pp);//ok,二级指针传参给,二级指针接收
 test(&p);//ok,二级指针存放一级指针的地址
 test(arr);//ok,指针数组传参,传递的是首元素的地址,元素类型是int,元素地址是int \*,二级指针能存放一级指针的地址
 return 0; }

五、函数指针

我们都知道函数指针变量是用来存放函数的地址的,那么函数的地址长什么样呢

在这里插入图片描述

1.1函数的地址和数组的地址有哪些地方不太一样

&数组名 - 数组的地址
数组名 - 数组首元素的地址

函数名 - 函数的地址
&函数名 - 函数的地址

1.2函数的地址又要存放到哪里去

void test()
{
 printf("hello word\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (\*pfun1)();
void \*pfun2();

pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void,函数指针的类型是int ( *)()。

1.3函数指针的使用

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

int main()
{
	int ret = Add(10,20);
	printf("%d\n", ret);

	int (\*pf)(int x, int y) = NULL;//定义函数指针变量
	pf = Add;//函数指针变量指向函数地址
	//pf = &Add; 可以写成这种方式
	ret = pf(20,30);//有了函数的地址,就可以调用该函数
	//ret = (\*pf)(20,30); 可以写成这种形式,便于初学者理解
	//ret = (\*\*\*\*\*\*pf)(20,30); 多加几颗\*也没有影响
	printf("%d\n",ret);
}

1.4阅读两段有趣的代码:

代码来自《C陷阱和缺陷》
在这里插入图片描述
代码一

先看代码一,0是整形,类型是int,(void () ())外面的一层小括号表示的是强制类型转换,而void()()表示的是一个类型,函数指针类型(void(*) ()) 0拼接起来的意思不就是将int型的0强制类型转换为
void( * ) ()类型的函数指针吗,有了了函数地址,再对指针解引用就能调用该函数,被调用的函数是一个无参,返回值类型为void的函数
以上代码确实符合语法,因为0地址处是被操作系统调用的,而程序员无法调用
总结:这是一次函数调用

代码二

这是一次函数声明,声明的函数名是signal,signal函数有两个参数,第一个是int类型,第二个是void( * )(int)的函数指针类型,signal函数的返回类型是void( * )(int)的函数指针类型

1.4.1代码二的简化写法
typedef void(\*pfunc\_t)(int);//给函数指针类型取别名为pfunc\_t
pfunc\_t signal(int,pfunc\_t);

这样子写起来是不是很简明,方便理解,熟练的使用typedef也可以解决这一类的问题

六、函数指针数组

函数指针数组的主体是一个数组,该数组存放的每一个元素是一个函数指针类型

1.1函数指针数组的初始化

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

int Sub(int x, int y)
{
	return x - y;
}

int Div(int x, int y)
{
	return x / y;
}

int Mul (int x, int y)
{
	return x \* y;
}

int main()
{
	int(\*pfArr[4])(int x, int y) = { Add ,Sub ,Mul ,Div};
	return 0;
}

怎么理解函数指针数组的语法呢?
int(*pfArr[4])(int x, int y)
首先【】的优先级是最高的,所以会先和pfArr结合,这一看就是一个数组嘛,数组的元素个数是4,数组的每一个元素类型就是int ( * )(int,int)类型的,那么我们可以知道数组的每一个元素都是一个函数指针,

这种初始化方式结合之前所学的函数指针和指针数组的知识相信屏幕前的你可以看懂,没错就是一个数组里面存放着的每一个元素都是一个函数指针,数组的元素个数是4,数组的每一个元素类型是int(*)(int,int)

1.2运用之前的知识

假设现在要实现一个简单的计算器程序
先按常规的写法实现一个计算器

void Menu() 
{
	printf("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*1、Add 2、Sub \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*3、Mul 4、Div \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\* 0、exit \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n");
}
int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		Menu();
		printf("请输入你的选择\n");
		scanf("%d",&input);
		switch (input)
		{
		case 0:
			printf("退出计算器\n");
			break;
		case 1:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = Add(x,y);
			printf("ret = %d\n",ret);
			break;
		case 2:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret = %d\n",ret);
			break;
		case 3:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret = %d\n",ret);
			break;
		case 4:
			printf("请输入两个操作数\n");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("ret = %d\n",ret);
			break;

		

default:
			printf("输入错误请重新输入\n");
			break;
		}
		

	} while (input);

  
	return 0;
}

简化版本

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

int Sub(int x, int y)
{
	return x - y;
}

int Div(int x, int y)
{
	return x / y;
}

int Mul (int x, int y)
{
	return x \* y;
}

void Menu() 
{
	printf("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*1、Add 2、Sub \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*3、Mul 4、Div \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\* 0、exit \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n");
}
int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		Menu();
		printf("请输入你的选择\n");
		scanf("%d",&input);
		
		int(\*pfArr[5])(int x, int y) = { 0,Add ,Sub ,Mul ,Div };
		
		if (!input)
		{
			printf("退出计算器\n");
			break;
		}
		else if (input >= 1 && input < 5)
		{
			printf("请输入两个操作数\n");
			scanf("%d %d",&x,&y);
			ret = pfArr[input](x, y);
			printf("ret = %d\n",ret);
		}
		else
		{
			printf("输入错误\n");
		}

	} while (input);


	return 0;
}

在这里插入图片描述
相比于之前的常规写法,这样的组织代码的方式,只让相同功能的代码只保存一份

可以看到函数指针数组的使用极大的降低了代码的冗余,而且可扩展性也能有所提升,将来要实现其他的功能,比如 >> 、<<、^、&=、这些计算都是可以的,只需要在函数指针数组初始化的时候再添加一个函数进去,再把else if分走语句的条件判断范围再给扩大那就ok,这种函数指针数组的使用方法在专业术语上叫做转移表

1.3通过回调函数的方式实现另外一种方案

1.4何为回调函数呢?

回调函数就是一个通过函数指针调用的函数。.
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。.
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

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

int Sub(int x, int y)
{
	return x - y;
}

int Div(int x, int y)
{
	return x / y;
}

int Mul (int x, int y)
{
	return x \* y;
}

void Menu() 
{
	printf("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*1、Add 2、Sub \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*3、Mul 4、Div \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\* 0、exit \*\*\*\*\*\n");
	printf("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\n");
}

void Calc(int(\*pf)(int x,int y)) 
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数\n");
	scanf("%d %d", &x, &y);
	ret = (\*pf)(x, y);
	printf("ret = %d\n", ret);
}
int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		Menu();
		printf("请输入你的选择\n");
		scanf("%d",&input);
		switch (input)
		{
		case 0:
			printf("退出计算器\n");
			break;
		case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;

		default:
			printf("输入错误请重新输入\n");
			break;
		}


	} while (input);


	return 0;
}

这样的代码组织方式也让相同功能的代码只保存一份

七、指向函数指针数组的指针

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

如果你需要这些资料,可以添加V无偿获取:hxbc188 (备注666)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
;
break;

	default:
		printf("输入错误请重新输入\n");
		break;
	}


} while (input);


return 0;

}



> 
> **这样的代码组织方式也让相同功能的代码只保存一份**
> 
> 
> 


## 七、指向函数指针数组的指针


### 最后

> **🍅 硬核资料**:关注即可领取PPT模板、简历模板、行业经典书籍PDF。  
> **🍅 技术互助**:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。  
> **🍅 面试题库**:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。  
> **🍅 知识体系**:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**如果你需要这些资料,可以添加V无偿获取:hxbc188 (备注666)**
[外链图片转存中...(img-ZCRrWNsW-1713852087879)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 21
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值