让人压抑的 C :记一个函数指针的问题
创始人
2025-07-07 14:31:46
0

最近因为项目要求用c++,之前一直很讨厌c++,没办法只能短时间弥补c++的知识,项目中需要定义一个函数指针类型的vector,本以为很简单的问题,结果调试了一天,才发现错在哪里。

多余的std::function

先上代码吧,这里有一个测试代码,为什么要有测试代码?是因为下面的方式我在最开始验证该种实现时打印的地址是对的,但是之后一段时间就不对了,所以摘出来写了一个测试代码。

代码非常简单:使用using std::function的方式定义一个函数指针类型func_t,然后实现三个print函数,在main函数中定义一个vector存放三个函数的地址,打印三个函数的实际地址,之后遍历vector打印存放的元素值。


#include 
#include 
#include 

// 定义 std::function 类型的函数指针别名
using func_t = std::function;

// 示例函数
void print(int x, void* y, size_t a, size_t b, void* c) {
    std::cout << "print hello\n";
}

void print1(int x, void* y, size_t a, size_t b, void* c) {
    std::cout << "print1 hello\n";
}

void print2(int x, void* y, size_t a, size_t b, void* c) {
    std::cout << "print2 hello\n";
}

int main() {
    // 创建一个存储 std::function 类型的函数指针对象的 std::vector
    std::vector vec;

    // 使用 push_back 将函数指针对象添加到 std::vector 中
    vec.push_back(print);
    vec.push_back(print1);
    vec.push_back(print2);

    printf("%x, %x, %x\n", print, print1, print2);

    // 遍历 std::vector 并依次调用存储的函数指针对象
    for (const auto& func : vec) {
        // 调用函数指针对象
        //func(0, nullptr, 0, 0, nullptr);
        printf("%x.\n", func);
    }

    return 0;
}

执行后的结果:

我最开始的理解是vector内部存放的地址就是三个函数的地址。结果打印的结果意料之外啊,居然一样,我尝试在for循环遍历时执行该地址函数,结果还能正常运行。最开始以为是vector遍历取值的问题,后来经过一番验证没问题,最后锁定要函数指针定义上。

我尝试切换一种函数指针定义,使用我最原始的方式:

// 定义 std::function 类型的函数指针别名
//using func_t = std::function;
using func_t = void (*)(int, void*, size_t, size_t, void*);

运行后发现这次是对的了:

最后经过一番查找,得出结论如下:

实际上,std::function 存储函数指针时,不直接存储函数指针本身的地址,而是存储了函数指针对象的一些信息,因此直接使用 %x 来打印 std::function 存储的函数指针可能无法获得正确的地址。

在标准库 中,std::function 是一个函数包装器,它可以包含各种可调用对象(函数指针、函数对象、成员函数指针、Lambda 表达式等)。因此,std::function 内部存储了被包装对象的地址以及其他信息,而不是直接将被包装对象的地址暴露给用户。

由于 std::function 对象的内部结构不同于原始函数指针, std::function 对象存储了更多的信息,所以直接打印 std::function 对象的地址并不会得到和原始函数指针相同的值,打印它的地址并不等同于打印函数指针的地址。

所以,如果需要存储函数指针并在之后通过 std::function 来调用它们,可以直接通过 std::function 来调用并且可以得到预期的结果,但是打印地址是不保证能够得到和原始函数指针相同的地址(这也是我遇到了几次和原始函数指针一致的时候,这也是造成我更迷茫的原因)。

那为什么打印的值一样呢?

因为在遍历 std::vector 时,即使它们指向不同的函数,它们的内部指针值可能是相同的,这是因为 std::function 可以包装不同的可调用对象,但它们内部可能使用相同的机制来存储函数指针或者函数对象的地址。因此,打印 std::function 内部存储的函数指针值可能会得到相同的结果。但这不应该影响 std::function 执行其持有的不同函数的能力。

总结

如果你也需要直接获取存储的函数指针的地址(C语言的习惯),最好还是直接使用原始的函数指针,而不是通过 std::function 来存储和获取函数指针的地址。

相关内容

热门资讯

PHP新手之PHP入门 PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的...
网络中立的未来 网络中立性是什... 《牛津词典》中对“网络中立”的解释是“电信运营商应秉持的一种原则,即不考虑来源地提供所有内容和应用的...
各种千兆交换机的数据接口类型详... 千兆交换机有很多值得学习的地方,这里我们主要介绍各种千兆交换机的数据接口类型,作为局域网的主要连接设...
全面诠释网络负载均衡 负载均衡的出现大大缓解了服务器的压力,更是有效的利用了资源,提高了效率。那么我们现在来说一下网络负载...
什么是大数据安全 什么是大数据... 在《为什么需要大数据安全分析》一文中,我们已经阐述了一个重要观点,即:安全要素信息呈现出大数据的特征...
如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
P2P的自白|我不生产内容,我... 现在一提起P2P,人们就会联想到正在被有关部门“围剿”的互联网理财服务。×租宝事件使得劳...
Intel将Moblin社区控... 本周二,非营利机构Linux基金会宣布,他们将担负起Moblin社区的管理工作,而这之前,Mobli...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...