C指针诡异代码深度解析,附例题

两段诡异代码

(*(void(*)())0)();
void (*signal(int , void(*)(int)))(int);

代码的出处《C陷阱和缺陷》
其实这些代码对于老司机来说肯定是见怪不怪,有点炒冷饭了。对于萌新来说简直是“我嘞个豆”。尤其是自己在学校上课老师讲得可能不太清楚,或者网课一笔带过导致似懂非懂。
所以我打算深度地去解析一下它!如果有讲的不好的地方,欢迎批评指点,谢谢!

代码1深度解析

(*(void(*)())0)();

这段代码是C语言中的一个复杂的表达式,它尝试调用一个函数,但这个函数的地址是0,也就是空指针。下面是对这段代码的分析:

0

这是一个整数常量,表示数字0。

(void(*)())

这是一个类型转换。它将整数0转换为一个函数指针的类型。这个函数指针的类型是指向一个返回类型为void且没有参数的函数。

(*(void(*)())0)

这是对转换后的函数指针进行解引用。解引用后得到的是一个函数,这个函数没有参数,返回类型为void。

(*(void(*)())0)()

最后,通过在解引用的函数后面加上一对括号()来调用这个函数。

在一些操作系统中,地址0是不可访问的,因为它通常被保留用于表示空指针错误(null pointer dereference)。尝试执行这段代码将导致程序崩溃,抛出一个访问违规(segmentation fault)或类似的错误。

#include <stdio.h>

void my_fc() {
    printf("Hello, World!\n");
}

int main() {
    // 正确版本
    void (*fc_ptr)() = my_fc;
    fc_ptr(); //调用

    printf("调用空指针函数之前\n");

    // 二号版本
    (*(void(*)())0)(); //导致崩溃发生

    // 如果程序能安全到达这里,说明没崩溃
    printf("博主是个靓仔(应该都知道什么是靓仔吧)\n");

    return 0;
}

Ok,我相信试了的同学,都没有看到博主是个靓仔
事后一想发现我为什么还要去试一下。总的来说,地址0又不包含有效的函数代码。或许是空指针保护,或许是无效的函数指针…

来个题目

int (*func_ptr)(int) = (int(*)(int))0x12345678;
func_ptr(10);

我嘞个豆x2

深度解析:

int (*func_ptr)(int)

这是一个函数指针的声明。func_ptr 是一个指针,它指向一个函数,这个函数接受一个 int 类型的参数并返回一个 int 类型的值。

(int(*)(int))0x12345678

这是一个类型转换。它将十六进制常量 0x12345678 转换为一个函数指针类型,这个类型与 func_ptr 的类型相匹配。这意味着它指向一个接受一个 int 参数并返回一个 int 的函数。

int (*func_ptr)(int) = (int(*)(int))0x12345678;

这行代码将转换后的地址赋值给 func_ptr 函数指针。现在,func_ptr 指向内存地址 0x12345678 处的函数。

func_ptr(10);

通过函数指针 func_ptr 调用函数,并传递参数 10。

总而言之,和上面的肯定是很类似的。这段代码的意图是调用位于内存地址 0x12345678 的函数,并将 10 作为参数传递给它。然而,潜在问题还是存在的:很多现代操作系统不给随便去访问任意内存地址。如果地址 0x12345678 不是有效的函数地址,或者该地址不是程序可执行内存的一部分,尝试调用这个地址的函数会导致未定义行为,然后导致程序崩溃。

代码2深度解析

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

这个其实我第一次看到时候没有我嘞个豆,除了可读性比较史,还是挺好看出来的。我的同学曾和我说:漂亮到极致的代码看不懂,史一样的我也看不懂,xx不读了。

回归正题

void (*)(int)

这是一个函数指针类型,指向一个接受一个 int 参数并返回 void 的函数。这种类型的函数指针通常用于信号处理函数,因为信号处理函数接受一个信号作为参数,并且不返回值。

signal(int, void(*)(int))

这是 signal 函数的声明。signal 是啥?signal函数接受两个参数:第一个参数是一个 int 类型,表示信号的编号;第二个参数是一个函数指针,指向信号处理函数,也就是上面提到的 void (*)(int) 类型。

void (*signal(int , void(*)(int)))(int)

这个整体声明表示 signal 函数返回一个函数指针,这个函数指针的类型与第二个参数的类型是一样的哦,指向一个接受一个 int 参数并返回 void 的函数。

其实第一段代码吃懂了,这个真的很好懂,当然了,对于老司机而言,啥也不是,对于我这种萌新而言,我啥也不是。
还没结束,这可读性也太差了,能改造一下么,可以!

void (*)(int)signal(int , void(*)(int))(int)

一下子就好看了,好看个xx,这是错的。
还是用 typedef 改造吧。

typedef改造行动

关于typedef,先来个例子:

typedef unsigned int uint;

接下来开始改造:

typedef void(*pfun_t)(int);

我对typedef的理解是*要吸住改造的成品,所以位置放的是不一样的。然后你再尝试用上面的改造方案来改造刚刚的代码。

pfun_t signal(int, pfun_t);

好看了!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值