与vsprintf和va_list的平台不一致

背景:我目前正在尝试“扩展”标准C格式,支持处理某个结构,类似于Objective-C扩展C格式以允许支持带有“%@”序列的NSString。

我正在努力解决的一个问题是vsprintf在OS X和Linux上的表现似乎不同(我用Ubuntu 10.10和12.04测试过)。 在OS X上,它表现得我认为应该如何,在调用vsprintf之后,调用va_arg返回ms指针(好像vsprintf函数调用va_arg来获取5)。 但是,在Linux上,va_list不会从vsprintf更改,并且调用va_arg将返回5。

我真的想找到一种方法来实现这个function,以便它在不同平台上表现一致。 假设您可以期望vsprintf始终更改va_list中的指针以便下次调用va_arg它返回下一个尚未使用的参数时,这是错误的吗?

我尽可能地简化了我的代码来演示这个问题。 在OS X上,此代码打印从malloc返回的指针的正确地址。 在Linux上,foo中ms的值变为5,因此它打印5。

#include  #include  #include  #include  static void foo(void *, ...); typedef struct { char *value; } mystruct; int main(int argc, char *argv[]) { mystruct *ms = malloc(sizeof(mystruct)); foo(NULL, "%d %@", 5, ms); } void foo(void *dummy, ...) { va_list args; va_start(args, dummy); char buffer[512]; int buffer_ptr = 0; int i = 0; char *format = va_arg(args, char *); buffer[0] = '\0'; for (i = 0; i < strlen(format); i++) { if (i <= strlen(format) - 1 && (format[i] == '%' && format[i+1] == '@')) { vsprintf(buffer, buffer, args); /* can expect the next argument to be a mystruct pointer */ mystruct *ms = va_arg(args, mystruct *); buffer[buffer_ptr+1] = '\0'; fprintf(stderr, "%p", ms); /* SHOULD NOT PRINT 5 */ /* concatenate here */ } else { buffer[buffer_ptr++] = format[i]; buffer[buffer_ptr] = '\0'; } } va_end(args); } 

如果要va_copy使用参数列表,则需要使用va_copy – 未执行此操作是未定义的行为。 您的代码应如下所示:

 va_list args; va_start(args, dummy); ... char *format = va_arg(args, char *); ... va_list argsCopy; va_copy(argsCopy, args); vsprintf(..., argsCopy); va_end(argsCopy); ... mystruct *ms = va_arg(args, mystruct *); ... va_end(args); 

问题在于实现如何实现va_list – 它可能包含直接提取参数的所有信息和状态,或者它可能包含指向间接保存状态的东西的指针。 因此将其传递给vsprintf可能会复制所有相关状态,也可能不会。

您想要做的是一个类似于vspintf的函数,它采用va_list *而不是va_list ,因此您可以确保在返回后具有正确的状态。 不幸的是,该标准没有提供任何此类function。