C++如何获取函数指针的地址?符号表与dladdr用法实战

来源:网络编程作者:狼行天下头衔:草根站长
导读:本期聚焦于小伙伴创作的《C++如何获取函数指针的地址?符号表与dladdr用法实战》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++如何获取函数指针的地址?符号表与dladdr用法实战》有用,将其分享出去将是对创作者最好的鼓励。

在C++程序开发中,获取函数指针的地址是动态链接、符号解析等场景下的常见需求,结合符号表与dladdr函数可以实现对函数地址对应符号信息的精准解析,下面我们将逐步展开相关内容的讲解。

C++如何获取函数指针的地址?符号表与dladdr用法实战

C++中获取函数指针地址的基础方法

在C++中,函数名本身就可以表示函数的入口地址,我们可以直接将函数名赋值给对应类型的函数指针,从而得到该函数的地址。首先需要明确函数指针的类型定义,要和函数的参数列表、返回值类型完全匹配。

比如我们有一个普通的函数:

#include <iostream>

// 定义一个普通的函数,返回值为int,参数为两个int
int add(int a, int b) {
    return a + b;
}

int main() {
    // 定义函数指针类型,匹配add函数的签名
    typedef int (*FuncPtr)(int, int);
    // 将函数名赋值给函数指针,得到函数地址
    FuncPtr ptr = add;
    std::cout << "函数add的地址为: " << (void*)ptr << std::endl;
    // 通过函数指针调用函数
    std::cout << "通过函数指针调用add(1,2)的结果: " << ptr(1, 2) << std::endl;
    return 0;
}

上述代码中,我们定义了匹配add函数签名的函数指针类型FuncPtr,将add函数赋值给ptr后,ptr中存储的就是add函数的入口地址,通过强制转换为void*类型可以打印出地址值。

符号表的基本概念

符号表是目标文件、可执行文件或动态链接库中的一个段,用于存储程序中定义和引用的符号信息,比如函数名、全局变量名、对应的地址、符号类型等。在Linux系统中,ELF格式的文件都包含符号表段,我们可以通过nmreadelf等命令查看文件的符号表信息。

符号表中的每一项都对应一个符号,包含符号名、符号值(通常是地址)、符号大小、符号类型等字段。当程序运行时,动态链接器会加载依赖的动态库,并将符号表中的信息映射到内存中,这为我们后续通过地址反向查找符号信息提供了基础。

dladdr函数的用法与实战

dladdr是Linux系统下动态链接库提供的一个函数,定义在dlfcn.h头文件中,它的作用是根据给定的地址,查找该地址对应的符号信息,包括符号名、符号所在的共享对象路径等。其函数原型如下:

#include <dlfcn.h>

int dladdr(void *addr, Dl_info *info);

参数说明:

  • addr:需要查询的地址,通常是我们获取到的函数指针地址。
  • info:输出参数,指向Dl_info结构体的指针,用于接收查询到的符号信息。

Dl_info结构体的定义如下:

typedef struct {
    const char *dli_fname;  // 包含该地址的共享对象的路径名
    void       *dli_fbase;  // 共享对象的加载基址
    const char *dli_sname;  // 距离addr最近的符号名
    void       *dli_saddr;  // 该符号对应的地址
} Dl_info;

函数返回值:如果找到了对应的符号信息,返回非0值;如果没有找到,返回0。

实战示例:通过函数指针地址查询符号信息

下面我们结合之前的函数指针知识,编写完整的示例代码,演示如何通过dladdr查询函数指针地址对应的符号信息:

#include <iostream>
#include <dlfcn.h>

// 定义测试函数
int add(int a, int b) {
    return a + b;
}

int main() {
    // 定义函数指针类型并获取add函数的地址
    typedef int (*FuncPtr)(int, int);
    FuncPtr ptr = add;
    void *func_addr = (void*)ptr;
    
    std::cout << "函数指针指向的地址: " << func_addr << std::endl;
    
    // 定义Dl_info结构体用于存储查询结果
    Dl_info info;
    // 调用dladdr查询地址对应的符号信息
    int ret = dladdr(func_addr, &info);
    if (ret != 0) {
        std::cout << "查询到符号信息:" << std::endl;
        std::cout << "共享对象路径: " << (info.dli_fname ? info.dli_fname : "null") << std::endl;
        std::cout << "共享对象加载基址: " << info.dli_fbase << std::endl;
        std::cout << "符号名: " << (info.dli_sname ? info.dli_sname : "null") << std::endl;
        std::cout << "符号地址: " << info.dli_saddr << std::endl;
    } else {
        std::cout << "未查询到对应的符号信息" << std::endl;
    }
    
    // 测试查询一个无效地址的情况
    void *invalid_addr = (void*)0x12345678;
    ret = dladdr(invalid_addr, &info);
    if (ret == 0) {
        std::cout << "无效地址未查询到符号信息,符合预期" << std::endl;
    }
    return 0;
}

编译上述代码时,需要链接动态链接库,使用如下编译命令:

g++ -o test_dladdr test_dladdr.cpp -ldl

运行生成的可执行文件,输出结果通常会包含add函数的符号名以及当前可执行文件的路径等信息,说明dladdr成功查询到了函数指针地址对应的符号信息。

注意事项

  • dladdr函数仅在Linux等支持POSIX标准的系统下可用,Windows系统没有对应的原生函数,需要使用其他方案如DbgHelp库实现类似功能。
  • 查询的地址必须是当前进程地址空间中有效的地址,且对应符号存在于符号表中,否则dladdr会返回0。
  • 如果程序编译时使用了-s选项或者strip命令去除了符号表,那么dladdr可能无法查询到对应的符号名,只能获取到共享对象的相关信息。
  • 静态函数的符号可能不会出现在动态符号表中,因此通过dladdr可能无法查询到静态函数的符号名。

总结

通过本文的讲解,我们了解了C++中获取函数指针地址的基础方法,掌握了符号表的基本概念,以及dladdr函数在解析函数地址对应符号信息时的具体用法。在实际开发中,如果需要实现动态符号解析、调试工具开发等功能,这些知识点可以发挥重要作用。使用时需要注意系统的兼容性以及符号表的存在情况,避免出现查询失败的问题。

C++函数指针符号表dladdr修改时间:2026-07-11 04:54:18

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。