c语言 函数名调用函数,在C语言里, 如何通过输入函数名字来调用函数?

这篇博客介绍了在C语言中动态调用函数的三种方法:1) 使用函数字典,但存在代码耦合问题;2) 利用nm或objdump在编译阶段输出符号信息,但需根据不同环境重新编译;3) 通过ELF查找符号表直接调用函数。作者详细讲解了第三种方法,即读取ELF文件的符号表来找到函数地址并执行。这种方法适用于多模块编译链接的场景。
摘要由CSDN通过智能技术生成

在C语言里, 如何通过输入函数名字来调用函数?

旧文重发。

大致有三种方法:

函数字典

缺点是代码耦合在一起, 无法复用。

#include #include #include #include void foo() { std::cout << "foo()"; }void boo() { std::cout << "boo()"; }void too() { std::cout << "too()"; }void goo() { std::cout << "goo()"; }int main() {std::map<:string std::function>> functions;functions["foo"] = foo;functions["boo"] = boo;functions["too"] = too;functions["goo"] = goo;std::string func;std::cin >> func;if (functions.find(func) != functions.end()) {functions[func]();}return 0;}

编译阶段将符号信息输出到打哪库

利用nm或者objdump, 在Makefile中在编译阶段将符号信息输出到源代码里. 缺点是每次在不同的环境里运行都要重新编译一次。

objs = main.o reflect.omain: $(objs)gcc -o $@ $^nm $@ | awk 'BEGIN{ print "#include "; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.cgcc -c .reflect.real.c -o .reflect.real.ogcc -o $@ $^ .reflect.real.onm $@ | awk 'BEGIN{ print "#include "; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.cgcc -c .reflect.real.c -o .reflect.real.ogcc -o $@ $^ .reflect.real.o

以上方法都可以在Stackoverflow上找到[1]

而我要给出的是另一种方法:

ELF查符号表, 找出函数的名字与值

背景知识可先看看:

ELF 是什么?

ELF(Executable and Linking Format) 是一个开放标准,各种 UNIX 系统的可执行文件都采用 ELF 格式,它有四种不同的类型:

•可重定位的目标文件 (Relocatable, 或者 Object File), Linux 的.o, Windows 的.obj•可执行文件 (Executable), Linux 的.out, Windows 的.exe•共享库 (Shared Object, 或者 Shared Library), Linux 的.so, Windows 的.DLL•核心转储文件 (Core Dump File) a, Linux 下的 core dump

方法大致是, 读取编译后的程序(可执行文件也是ELF), 找到 SHT_SYMTAB(符号表), 然后遍历符号表, 找到与函数名一样的符号.

因为现在的C语言已经不会在符号前加上下划线了, 所以可以名字与符号名相同.

符号是什么?

void (*fun)(void) = (void*)sym.st_value;(*fun)();

所有 extern 函数的符号都会存在可执行文件中, 所以即便是多个模块的编译链接, 这个函数依然适用.

遍历符号表

遍历符号表的代码如下:

#include #include #include #include #include #include voidmain(int argc, char **argv){Elf *elf;Elf_Scn *scn = NULL;GElf_Shdr shdr;Elf_Data *data;int fd, ii, count;elf_version(EV_CURRENT);fd = open(argv[1], O_RDONLY);elf = elf_begin(fd, ELF_C_READ, NULL);while ((scn = elf_nextscn(elf, scn)) != NULL) {gelf_getshdr(scn, &shdr);if (shdr.sh_type == SHT_SYMTAB) {/* found a symbol table, go print it. */break;}}data = elf_getdata(scn, NULL);count = shdr.sh_size / shdr.sh_entsize;/* print the symbol names */for (ii = 0; ii < count; ++ii) {GElf_Sym sym;gelf_getsym(data, ii, &sym);printf("%s\n", elf_strptr(elf, shdr.sh_link, sym.st_name));}elf_end(elf);close(fd);}

只需在找到符号的代码里,加上:

if(!strcmp(sym_name, function_name)) {void (*fun)(void) = (void*)sym.st_value;(*fun)();}

References

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值