调用具有不同数量参数的 cdecl 函数

Posted

技术标签:

【中文标题】调用具有不同数量参数的 cdecl 函数【英文标题】:Calling cdecl Functions That Have Different Number of Arguments 【发布时间】:2011-01-26 12:49:17 【问题描述】:

我有一些我希望根据一些输入调用的函数。每个函数都有不同数量的参数。换句话说,

if (strcmp(str, "funcA") == 0) funcA(a, b, c);
else if (strcmp(str, "funcB") == 0) funcB(d);
else if (strcmp(str, "funcC") == 0) funcC(f, g);

这有点笨重且难以维护。理想情况下,这些是可变参数函数(例如 printf 样式)并且可以使用可变参数。但他们不是。因此,利用 cdecl 调用约定,我通过一个充满参数的结构填充堆栈。我想知道是否有更好的方法来做到这一点。请注意,这仅限于内部(例如,简单工具、单元测试等),不会用于任何可能受到恶意攻击的生产代码。

例子:

#include <stdio.h>

typedef struct __params

    unsigned char* a;
    unsigned char* b;
    unsigned char* c;
 params;

int funcA(int a, int b)

    printf("a = %d, b = %d\n", a, b);
    return a;


int funcB(int a, int b, const char* c)

    printf("a = %d, b = %d, c = %s\n", a, b, c);
    return b;


int funcC(int* a)

    printf("a = %d\n", *a);
    *a *= 2;
    return 0;


typedef int (*f)(params);

int main(int argc, char**argv)

    int val;
    int tmp;
    params myParams;
    f myFuncA = (f)funcA;
    f myFuncB = (f)funcB;
    f myFuncC = (f)funcC;

    myParams.a = (unsigned char*)100;
    myParams.b = (unsigned char*)200;

    val = myFuncA(myParams);
    printf("val = %d\n", val);

    myParams.c = (unsigned char*)"This is a test";
    val = myFuncB(myParams);
    printf("val = %d\n", val);

    tmp = 300;
    myParams.a = (unsigned char*)&tmp;
    val = myFuncC(myParams);
    printf("a = %d, val = %d\n", tmp, val);
    return 0;

输出:

gcc -o func func.c
./func
a = 100, b = 200
val = 100
a = 100, b = 200, c = This is a test
val = 200
a = 300
a = 600, val = 0

【问题讨论】:

对于 C++,我可以给你一个相对安全的答案,但对于 C,除非你调用可变参数函数,否则你不能保证每个参数在堆栈上的大小都相同。 栈上的参数不是被强制转换/保存为内存对齐的(例如,32 位、64 位),因此大小相同吗? 【参考方案1】:

与其试图在语言中的极端情况下折磨这种情况,我会以一种更直接的方式来做到这一点,即定义可变参数包装函数。你可以用宏来做这件事:

#define WRAP_VARIADIC_2_ARG(FNAME, RETTYPE, ARGTYPE1, ARGTYPE2) \
  (RETTYPE) wrapped_##FNAME(...)                               \
    va_list args;                                               \
    va_start(args, 2);                                          \
    (ARGTYPE1) arg1 = va_arg(args, (ARGTYPE1));                 \
    (ARGTYPE2) arg2 = va_arg(args, (ARGTYPE2));                 \
    va_end(args);                                               \
    return FNAME(arg1, arg2);                                   \
  

以及其他 arg 计数的类似宏。然后,您调用:

WRAP_VARIADIC_2_ARG(funcA, int, int, int)
WRAP_VARIADIC_3_ARG(funcB, int, int, int, const char*)
WRAP_VARIADIC_1_ARG(funcC, int, int*)

这将定义一组具有以下签名的函数,您可以在调度函数中使用它们:

int wrapped_funcA(...)
int wrapped_funcB(...)
int wrapped_funcC(...)

从那里应该很简单。

【讨论】:

以上是关于调用具有不同数量参数的 cdecl 函数的主要内容,如果未能解决你的问题,请参考以下文章

CDECL 调用函数内。获得参数个数。

C调用约定__cdecl__stdcall__fastcall__pascal分析

__stdcall__cdecl

调用约定_stdcall _cdecl _fastcall的区别

函数调用方法之__cdecl与_stdcall

_cdel stdcall