在C语言中,给定一个可变参数列表,如何使用它们构建函数调用?
假设有一个以某种方式存储的参数列表,例如在数组中 给定一个函数指针,如何通过存储的参数列表调用它 我不想把数组作为参数传递,ok。你明白了,好吗?我想把它的每个元素作为参数传递。数组只是为了说明,我可以将参数存储在某个元组结构中。另外,请注意,我手头有一个函数指针,可能有一个字符串格式的签名。我并不是要定义一个能够处理变量列表的函数 我知道如何做到这一点的唯一方法是采用汇编(通过在C语言中,给定一个可变参数列表,如何使用它们构建函数调用?,c,assembly,ffi,libffi,C,Assembly,Ffi,Libffi,假设有一个以某种方式存储的参数列表,例如在数组中 给定一个函数指针,如何通过存储的参数列表调用它 我不想把数组作为参数传递,ok。你明白了,好吗?我想把它的每个元素作为参数传递。数组只是为了说明,我可以将参数存储在某个元组结构中。另外,请注意,我手头有一个函数指针,可能有一个字符串格式的签名。我并不是要定义一个能够处理变量列表的函数 我知道如何做到这一点的唯一方法是采用汇编(通过\uu asm push等)或以下方法: void(*f)(…); int main() { f=; int-args
\uu asm push
等)或以下方法:
void(*f)(…);
int main()
{
f=;
int-args[];
int num_args=;
开关(num_args)
{
案例0:
f();
打破
案例1:
f(args[0]);
打破
案例2:
f(args[0],args[1]);
打破
/*等*/
}
返回0;
}
我不太喜欢这种方法
有没有另一种可移植的较短形式
有几种脚本语言能够调用C函数
Python或Ruby等脚本语言是如何做到这一点的?他们如何以可移植的方式实现它?他们最终是使用多个平台还是以上平台的组装
看,我并不是在问参数封送的细节以及从脚本语言到C的其他东西,我只感兴趣的是,最终,脚本语言对C函数的调用是如何在内部构建的
编辑
我将保留问题的标题,但我认为更好的提问方式是:
如何调用C函数,其指针和签名仅在运行时可用
更新
发件人:
调出是一种正常的函数调用。在动态环境中,
我们创建一个“调用接口”对象,它指定(二进制)
输入/输出类型;此对象可以与任意
函数指针和输入值数组,用于执行函数调用并检索其结果。这样做需要
操纵堆栈并了解如何调用函数,
这些是libffi处理的细节
感谢@AnttiHaapala的搜索、查找和指点。这正是我所寻找的,它正被许多脚本语言所使用,它是一个可移植的库,跨多个体系结构和编译器实现。为了安全起见,您应该在发送变量之前将其解压缩。使用汇编程序破解参数堆栈可能无法在编译器之间移植。调用约定可能会有所不同 我不能代表Ruby说话,但我已经使用Perl和Python的C接口编写了很多程序。Perl和Python变量不能与C变量直接比较,它们有更多的特性。例如,Perl标量可能具有双字符串和数字值,在任何时候只有其中一个是有效的
Perl/Python变量和C之间的转换是使用
pack
和unpack
完成的(在Python的struct
模块中)。在C接口上,您必须根据类型调用特定的API来进行转换。因此,它不仅仅是一个直接的指针传输,当然也不涉及汇编程序。您问过,使用给定数量的参数调用任何函数指针的可移植方式是什么。正确的答案是没有这种方法
例如,python能够通过ctypes模块调用C函数,但这是可移植的,只要您知道确切的原型和调用约定。在C语言中,实现这一点的最简单方法是在编译时知道函数指针的原型
更新
对于python/ctypes示例,在每个启用了ctypes模块的平台上,python都知道如何为给定的参数集编写调用堆栈。例如,在Windows上,python知道两种标准的调用约定——堆栈上参数的C顺序的cdecl和带有“pascal样式顺序”的stdcall。在Linux上,它确实需要考虑是调用32位还是64位共享对象,等等。如果python被编译到另一个平台,那么CType也需要更改;ctypes模块中的C代码本身是不可移植的
更新2
对于Python来说,魔法就在这里:。值得注意的是,它似乎将你所需要的东西联系起来。@AnttiHaapala指出。以下是关于它的一些信息:
什么是Liffi?
有些程序在编译时可能不知道要传递给函数的参数。例如,可以在运行时告诉解释器用于调用给定函数的参数的数量和类型libffi’可用于此类程序中,以提供从解释器程序到编译代码的桥梁
“libffi”库为各种调用约定提供了一个可移植的高级编程接口。这允许程序员在运行时调用调用接口描述指定的任何函数
FFI代表外部功能接口。外部函数接口是接口的流行名称,它允许用一种语言编写的代码调用用另一种语言编写的代码。“libffi”库实际上只提供功能齐全的外部函数接口中最低的、依赖于机器的层。“libffi”上方必须存在一个层,该层处理两种语言之间传递的值的类型转换
“libffi”假设您有一个指向要调用的函数的指针,并且您知道要传递它的参数的数量和类型,以及函数的返回类型
历史背景 libffi最初由Anthony Green(SO用户:)开发,其灵感来源于Silicon Graphics的Gencall库。Gencall由Gianni Mariani开发
void (*f)(...);
int main()
{
f = <some function pointer>;
int args[]; <stored in a array, just to illustrate>
int num_args = <some value>;
switch(num_args)
{
case 0:
f();
break;
case 1:
f(args[0]);
break;
case 2:
f(args[0], args[1]);
break;
/* etc */
}
return 0;
}
#include <stdio.h>
#include <math.h>
#include <ffi.h>
int main()
{
ffi_cif call_interface;
ffi_type *ret_type;
ffi_type *arg_types[2];
/* pow signature */
ret_type = &ffi_type_double;
arg_types[0] = &ffi_type_double;
arg_types[1] = &ffi_type_double;
/* prepare pow function call interface */
if (ffi_prep_cif(&call_interface, FFI_DEFAULT_ABI, 2, ret_type, arg_types) == FFI_OK)
{
void *arg_values[2];
double x, y, z;
/* z stores the return */
z = 0;
/* arg_values elements point to actual arguments */
arg_values[0] = &x;
arg_values[1] = &y;
x = 2;
y = 3;
/* call pow */
ffi_call(&call_interface, FFI_FN(pow), &z, arg_values);
/* 2^3=8 */
printf("%.0f^%.0f=%.0f\n", x, y, z);
}
return 0;
}