C 如何以可移植的方式将可变参数列表传递给vprintf?

C 如何以可移植的方式将可变参数列表传递给vprintf?,c,gcc,abi,gcc4.7,C,Gcc,Abi,Gcc4.7,我有一个代码,它从设备接收32位值的二进制数组,并用vsprintf打印它们,如下所示: void print_stuff(int32_t *p, const char *format) { vprintf( format, (va_list)p); } void print_stuff(const char *format, ...) { va_list args; va_start(args, format); vprintf(format, args);

我有一个代码,它从设备接收32位值的二进制数组,并用vsprintf打印它们,如下所示:

void print_stuff(int32_t *p, const char *format)
{
    vprintf( format, (va_list)p);
}
void print_stuff(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
const char *fmt = "%x %x %x %x\n";
...
print_stuff(fmt, data[0], data[1], data[2], data[3]);
这是简化的;确保值与格式匹配,等等

基本上,这取决于在普通x86中,va_列表只是一个指针或数组。这将在没有警告的情况下编译

现在我需要将它移植到ARMLinuxGnueAbiHF和x64,它甚至不编译。GCC 4-something for ARM表示错误:请求转换为非标量类型

如何以可移植的方式从二进制数组生成va_列表?或者至少分别针对32位和64位ARCH,是否可以不使用任何本机调用接口库? 如果这是不可能的,那么是否有任何其他标准或GNU库函数适合此任务

调用此函数的代码示例:

#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#ifndef __GNUC__
#error Only Linux, other platforms/compilers not needed
#endif

int main() {
uint32_t data[10];
int i;
const char *fmt = "%x %x %x %x\n";
// Simulation of reading the data
for(i = 0; i < 10, i++) data[i] = 0x100 + i;
print_stuff(data, fmt);
return 0;
}
将突出的评论转换为近似的答案


您必须检查和修改呼叫代码才能正确完成工作。或者你放弃了对vprintf的简单呼叫,更加努力地工作

如果将数组传递给打印函数,则不会传递va_列表。在某些特定的实现中,会发生各种各样的事情,而不是在所有的实现中都会发生。你会发现,你目前的系统在它的生态位中工作,但现在它需要离开它的生态位,它不再工作了。欢迎来到“未定义行为”的世界。使用未定义行为的代码不需要工作;它也不一定要失败

即使你修改了问题,也没有可移植的方法。时期数组中有多少个条目?格式字符串是什么样子的?要打印的项目数量是否固定?基本上,您可以简化为:

int n_fields = count_fields(format);
switch (n_fields)
{
case 1: printf(format, p[0]); break;
case 2: printf(format, p[0], p[1]); break;
…
}
这是便携式和可靠的。如果数组中的项目数是固定的,那么您的代码将显示使用的4个项目,当然,您不需要进行计数或切换


你可以查阅;它可能会对您有所帮助。

因为格式是预定义的,所以您确切地知道要传入多少个参数。因此,将所需的参数准确地传递给函数,而不是传递数组。然后将函数更改为使用。。。作为一个参数,并从中获取一个va_列表

因此,您的函数如下所示:

void print_stuff(int32_t *p, const char *format)
{
    vprintf( format, (va_list)p);
}
void print_stuff(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
const char *fmt = "%x %x %x %x\n";
...
print_stuff(fmt, data[0], data[1], data[2], data[3]);
你会这样称呼它:

void print_stuff(int32_t *p, const char *format)
{
    vprintf( format, (va_list)p);
}
void print_stuff(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
const char *fmt = "%x %x %x %x\n";
...
print_stuff(fmt, data[0], data[1], data[2], data[3]);

为什么你会做一些你显然不应该做的事情?这段代码最初是谁写的?那个人应该受到严厉的批评!最后期限?我想你拼错了“无能”。十几岁的时候,当你觉得自己很聪明,并且把一切都弄明白的时候,做这样的事情是很有趣的。然后你遇到了第一个可移植性问题,不再聪明了。你做不到。设计一个合适的界面来完成你需要做的事情,而不是在你已经深陷其中的时候更深入地挖掘。你必须检查和修改调用代码才能正确地完成这项工作。或者你放弃了对vprintf的简单调用,更加努力地工作。许多年前,我也使用了这种方法来转换va_列表,它在i386和其他平台上工作,但有一天我遇到了一个具有不同va_列表实现的平台。所以我的功能停止工作了。结论:你应该按常规工作!Jonathan Leffler早些时候在评论中给出了这个解决方案。我会把它标记为答案。谢谢谢谢你,这个解决方案对我有用。为简单起见,未显示参数的数量。它需要在堆栈上推送参数,这在我的系统中是有限的-但对于少量参数将起作用。-dd