C #定义具有可变长度参数的和函数

C #定义具有可变长度参数的和函数,c,C,在一个巨大的程序中,我有无数的函数调用。现在我想将它们全部转换,这样函数就可以接受一个新的整数参数(称为x),而不必编辑无数的调用。如果我的_printf只接受了一个字符串参数,那么我可以这样做: #define my_printf(str) _my_printf(x,str) void _my_printf(int x,char *str) // changed from my_printf(char *str) { // stuff } 但

在一个巨大的程序中,我有无数的函数调用。现在我想将它们全部转换,这样函数就可以接受一个新的整数参数(称为x),而不必编辑无数的调用。如果我的_printf只接受了一个字符串参数,那么我可以这样做:

    #define my_printf(str) _my_printf(x,str)

    void _my_printf(int x,char *str) // changed from my_printf(char *str)
    {
        // stuff
    }
但是,由于我的_printf接受了数量可变的参数,我不知道该怎么做。能做到吗

编辑:对于那些想知道我为什么要做这样一件事的人,这里有一个相关的例子:

#if BELT_AND_BRACES_DIAGNOSTIC_MODE
    #define function(x) _function(__FILE__,__LINE__,x)
#else // speed critical optimised mode
    #define function(x) _function(x)
#endif

#if BELT_AND_BRACES_DIAGNOSTIC_MODE
    void _function(char *file,int line,int x)
#else
    void _function(int x)
#endif
{
    // stuff
    #if BELT_AND_BRACES_DIAGNOSTIC_MODE
    if (something_went_wrong)
    {
        printf("Cock up in function when called from %s line %d\n",file,line);
    }
    #endif
}

当然,最好的办法是咬紧牙关编辑代码。否则,您将创建一个“谜”,需要所有未来的代码维护人员来解决。即使只有你一个人,这也正是一种你会完全忘记的聪明把戏。回来的感觉很糟糕,对奇怪的毫无意义的宏感到困惑


这就是说,如果您使用的是GNU工具链,您也许可以考虑使用。

如果代码可以编译为C99代码,您可以定义


预处理器将替换参数。。。如果只使用str参数调用宏,GNU预处理器将删除尾随的逗号。

如果
my_printf
已经使用了可变数量的参数,我不确定为什么需要在宏中包装“另一个参数”。。。只需插入带有额外参数的新调用,并完成它;旧的调用仍应按预期工作。

不适用于标准C89宏,您不能。但是,通过将
my_printf
函数的主要部分分解为
vmy_printf
函数,您可以使用函数获得相同的效果,这与标准
vprintf
类似:

#include <stdarg.h>

int vmy_printf(int x, const char *format, va_list ap)
{
    /* main body of my_printf goes here, taking its varargs from ap */
}

/* new_my_printf(), for callers who know about the x parameter */
int new_my_printf(int x, const char *format, ...)
{
    int n;
    va_list ap;

    va_start(ap, format);
    n = vmy_printf(x, format, ap);
    va_end(ap);

    return n;
}

/* my_printf(), for the old callers who don't know about x */
int my_printf(const char *format, ...)
{
    int n;
    va_list ap;

    va_start(ap, format);
    n = vmy_printf(DEFAULT_X, format, ap);
    va_end(ap);

    return n;
}
#包括
int vmy_printf(int x,const char*格式,va_list ap)
{
/*my_printf的主体在这里,从ap获取其变量*/
}
/*new_my_printf(),适用于了解x参数的调用方*/
int new_my_printf(int x,常量字符*格式,…)
{
int n;
va_列表ap;
va_开始(ap,格式);
n=vmy_printf(x,格式,ap);
va_端(ap);
返回n;
}
/*my_printf(),用于不了解x的老呼叫者*/
int my_printf(常量字符*格式,…)
{
int n;
va_列表ap;
va_开始(ap,格式);
n=vmy_printf(默认值X,格式,ap);
va_端(ap);
返回n;
}

(这就是为什么所有标准varargs函数的v…版本都存在的原因。)

您可以使用C99可变宏:

#define my_printf(...) my_printf_(x, __VA_ARGS__)
作为禁止尾随逗号,可以显式添加
str
参数

#define my_printf(str, ...) my_printf_(x, str, __VA_ARGS__)
但是,在没有可变参数的情况下调用标准C时,这将导致语法错误

my_printf("foo")
或者是一个空的参数列表

my_printf("foo",)

因此,我选择第一个版本。

解决这个问题的简单方法是

#define my_printf(x) printf x
(注意缺少的大括号)

要调用它,请使用:

my_printf((any number of arguments))

(注意双支架)

+1以咬住子弹。今天的编辑器支持重构等等,你应该会笑。见鬼,15年前,我的编程编辑器可以很容易地在整个目录树中对通配符指定的文件进行全局搜索和替换,无论是否先预览结果。(如果你想的是宏观的,那么你可以盲目地去做。)+1。把它看作是一个学习机会,让你真正了解现代编辑可以做什么(或者如果你已经在这里呆了足够长的时间,Vim或Emacs可以做什么——他们可以做Visual Studio刚刚赶上的事情……)。这是GNU,而不是C99-C99版本应该是
define my_printf(str…)\u my_printf(x,\u VA_ARGS)
;请记住,您需要至少提供一个参数。此外,您和我都忘了传递
str
;我认为最好的方法实际上是完全删除显式的
str
,只使用
定义my_printf(…)\u my_printf(x,\uu VA_ARGS\uuuu)
,这样即使在C99版本中也可以不用函数的varargs调用它。问题现在已经解决了。我使用的是dev studio 2008,VA_ARGS这个东西很管用——但我不确定哪一个答案是正确的,因为它们都不是完全正确的。unwind的答案意味着我需要使用GNU,phillipe的答案也有错误。@Mick:我添加了一个答案,其中有一些关于空参数列表和尾随逗号问题的详细信息:@philippe:use
-std=c99-pedantic
,以获取警告
my_printf((any number of arguments))