【C语言进阶】C语言指针进阶实战:优化与难题解析

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C语言 “ 登神长阶 ”
🤡往期回顾🤡:C语言指针进阶 (上)
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


前言:在C语言的浩瀚宇宙中,指针无疑是那颗最为璀璨而神秘的星辰。它既是连接数据与操作的桥梁,也是让许多初学者望而生畏的迷宫。一旦掌握了指针的精髓,C语言的世界便仿佛被彻底点亮,编程的效率和灵活性得以质的飞跃。然而,仅仅停留在指针的基本使用上,远不足以探索其全部魅力与力量。指针的进阶应用,尤其是如何通过指针优化程序性能、解决复杂难题,是每一位C语言开发者必须攀登的高峰

每个实战案例都将配以详细的代码示例与解释,旨在让读者不仅能够理解其背后的原理,更能够亲手实践,将所学知识转化为解决问题的能力。同时,我们也会对一些常见的指针难题进行深度解析,比如指针运算的陷阱、多级指针的理解难点等,帮助读者彻底克服这些学习障碍

让我们继续一同踏上这场充满挑战与收获的指针进阶之旅吧!


📒1. 函数指针

C语言中的函数指针是一种特殊的指针类型,它存储的不是变量的地址,而是函数的地址。通过函数指针,我们可以在运行时动态地调用函数,这增加了程序的灵活性和模块化。函数指针在回调函数、中断服务例程、以及实现函数表(如多态)等场景中非常有用

void test()
{
	printf("hehe\n");
}

int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

在这里插入图片描述

输出的是两个地址,这两个地址是 test 函数的地址,那么我们怎么保存函数的地址呢?

定义:

// 保存函数的地址
void (*pfun1)();

解析:pfun1可以存放存储地址。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void


我们再来看一下这两个代码

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

代码1:

这段代码尝试执行一个位于地址0处的函数。首先,(void (*)())0是一个类型转换,它将整数0转换为指向返回类型为void且不接受任何参数的函数的指针。然后,*操作符对转换后的指针进行解引用,尝试获取该函数指针指向的函数。最后,()用于调用该函数

代码2:

  • signal是一个函数,它接受两个参数:
    • 第一个参数是int类型,通常用于指定要处理的信号编号。
    • 第二个参数是一个指向函数的指针,这个函数接受一个int参数(通常是信号编号)并返回void。
  • signal函数的返回类型是一个指向函数的指针,这个函数也接受一个int参数并返回void

📙2. 函数指针数组

把函数的地址存到一个数组中,那这个数组就叫函数指针数组

定义:

int (*parr1[10])();
// parr1 先和 [] 结合,说明 parr1是数组
// 存放int (*)() 类型的函数指针

举个例子来展示如何定义一个函数指针数组,并向其中添加函数指针,最后通过索引来调用这些函数

// 定义三个示例函数  
void func1(int x) 
{
    printf("Func1 called with %d\n", x);
}

void func2(int x) 
{
    printf("Func2 called with %d\n", x);
}

void func3(int x) 
{
    printf("Func3 called with %d\n", x);
}

// 定义一个指向函数的指针类型,该函数接受一个int参数并返回void  
typedef void (*FuncPtr)(int);

int main() {
    // 创建一个函数指针数组,可以存储三个指向函数的指针  
    FuncPtr funcArr[3] = { func1, func2, func3 };

    // 通过索引调用函数  
    for (int i = 0; i < 3; i++) {
        funcArr[i](i + 1); // 调用函数,并将索引值+1作为参数传递  
    }

    return 0;
}

📚3. 指向函数指针数组的指针

指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针

定义:

void test(const char* str)
{
	printf("%s\n", str);
}

int main()
{
	//函数指针pfun
	void (*pfun)(const char*) = test;
	
	//函数指针的数组pfunArr
	void (*pfunArr[5])(const char* str);
	pfunArr[0] = test;
	
	//指向函数指针数组pfunArr的指针ppfunArr
	void (*(*ppfunArr)[5])(const char*) = &pfunArr;
	
	return 0;
}

📜4. 回调函数

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

// 定义一个回调函数的类型,该函数接受一个int参数并返回void  
typedef void (*CallbackFunc)(int);

// 一个需要回调函数的函数  
void processData(int data, CallbackFunc callback) 
{
    printf("Processing data: %d\n", data);
    // 调用回调函数  
    callback(data);
}

// 用户定义的回调函数  
void myCallback(int data) {
    printf("Callback called with data: %d\n", data);
}

int main() {
    // 调用需要回调函数的函数,并传递用户定义的回调函数作为参数  
    processData(10, myCallback);

    return 0;
}

processData函数接受一个整数和一个回调函数作为参数。在processData函数内部,首先执行一些处理,然后调用回调函数callback,并将之前接收到的整数data作为参数传递给回调函数。用户定义的回调函数myCallback被传递给processData函数,并在适当的时候被调用

回调函数广泛应用于事件处理、排序算法(如快速排序中的比较函数)


📝5. 指针笔试题

题目一:

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}

在这里插入图片描述


题目二:

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

数组初始化时使用的 (0, 1) 形式的初始化实际上是逗号运算符(comma operator)的使用。逗号运算符会评估其两个操作数,但只返回最后一个操作数的值

这里 p 被赋值为 a[0] 的地址,即 &a[0][0]。然后 p[0] 访问了这个地址的内容,即 a[0][0],其值为1
在这里插入图片描述


题目三:

int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

这道题目比较简单,各位可以自己看看


题目四:

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a)); // 16 数组大小
printf("%d\n",sizeof(a+0)); // 4 数组元素的大小
printf("%d\n",sizeof(*a)); // 4 首元素大小
printf("%d\n",sizeof(a+1)); // 4 数组元素的大小
printf("%d\n",sizeof(a[1])); // 4 数组元素的大小
printf("%d\n",sizeof(&a)); // 4 数组首元素的大小
printf("%d\n",sizeof(*&a)); // 16 数组大小
printf("%d\n",sizeof(&a+1)); // 4 数组元素的大小
printf("%d\n",sizeof(&a[0])); // 4 数组元素的大小
printf("%d\n",sizeof(&a[0]+1)); // 4 数组元素的大小

📖6. 总结

随着指针进阶学习的深入,我们仿佛揭开了C语言世界中最为神秘而又引人入胜的一角。在上一部分,我们已经见证了指针如何成为连接数据与内存的桥梁,而在这部分,我们更是将这份力量发挥到了极致

回顾这段学习之旅,我们不难发现,指针不仅是C语言的核心特性之一,更是编程世界中一把强大的双刃剑。它既能让我们实现高效的内存管理与复杂的数据操作,也可能因不当使用而引发难以察觉的错误与漏洞。因此,在享受指针带来的便利与乐趣的同时,我们也必须时刻保持警惕,遵循最佳实践,确保代码的安全与可靠

至此,我们的C语言指针进阶之旅即将画上圆满的句号。但请记住,学习永无止境,技术的海洋浩瀚无垠。愿每一位编程爱好者都能保持对技术的热爱与追求,继续在编程的道路上探索前行,创造属于自己的辉煌篇章

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

  • 20
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值