这个递归的va_arg代码有什么问题?

时间:2010-07-29 21:10:17

标签: c++ c

我正在尝试使用变量参数列表创建泛型函数。设计的一部分是这些功能中的一些相互调用。不幸的是它似乎不起作用。如您所见,如果您运行下面的简单代码,对command()的调用总是失败,但是直接调用marshal_size()会根据格式字符串“FORMAT_STRING”成功解码两个字符串“FIRST_STR_ARG”和“SECOND_STR_ARG”

我的推理有什么问题?

示例代码与“g ++ main.cpp”或“gcc main.c”编译良好。

谢谢,

#include <stdarg.h>
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>

#define MARSHAL_FORMAT "%s%s"
#define FIRST_STR_ARG "THIS_IS_ARG_ONE"
#define SECOND_STR_ARG "THIS_IS_ARG_TWO"

#define d(msg__, ...) do { printf("%s@%d: "msg__"\n", __FILE__, __LINE__, ## __VA_ARGS__); } while (0)

static uint32_t
marshal_size(const char *format, ...)
{
    uint32_t retv = 0;
    uint8_t ub;
    uint16_t uw;
    uint32_t ul;
    char *s;
    va_list ap;

    if (!format || !strlen(format))
            return 0;
    d("format = %s \n", format);

    va_start(ap, format);
    for (; '\0' != *format; format++) {

            d("*format = %c \n", *format);
            if ('%' == *format) {
                    format++;
                    if ('u' == *format)
                            format++;
            } else {
                    d("FORMAT ERROR\n");
                    continue;
            }
            d("*format = %c \n", *format);

            switch (*format) {
            case 's':
                    s = va_arg(ap, char*);
                    d("va_arg = %s\n", (s ? s : "NULL"));
                    if (s)
                            retv += strlen(s) + 1;
                    break;
            case 'l':
                    ul = va_arg(ap, uint32_t);
                    retv += sizeof(uint32_t);
                    break;
            case 'w':
                    uw = (uint16_t)va_arg(ap, int);
                    retv += sizeof(uint16_t);
                    break;
            case 'b':
                    ub = (uint8_t)va_arg(ap, int);
                    retv += sizeof(uint8_t);
                    break;
            default:
                    goto exit;
            }

            continue;
    exit:
            break;
    }

    va_end(ap);

    return retv;
}

static uint32_t
command(const char * const format,
    ...)
{
    uint32_t retv;
    va_list ap;

    va_start(ap, format);

    retv = marshal_size(format, ap);

    va_end(ap);

    return retv;
}

int
main(int argc, char *argv)
{
    uint32_t size;

    size = command(MARSHAL_FORMAT, FIRST_STR_ARG, SECOND_STR_ARG);
    d("size = %d", size);

    size = marshal_size(MARSHAL_FORMAT, FIRST_STR_ARG, SECOND_STR_ARG);
    d("size = %d", size);

    return EXIT_SUCCESS;
}

4 个答案:

答案 0 :(得分:6)

您必须marshal_sizeva_list而不是...。请参阅c-faq.com常见问题解答中的Question 15.12

答案 1 :(得分:3)

我认为你不能将va_list传递给带有变量参数的函数。我相信你需要另一个marshal_size版本,它将va_list作为一个硬参数。

我对这一切如何在下面工作的细节有点生疏。所以我不会尝试解释。但作为支持,我建议这就是我们拥有此类函数的vprintf, vfprintf, and vsprintf版本的原因。

您很可能会marshal_size(const char *format, ...)调用marshal_size(const char *format, va_list arg_ptr)来实际执行其功能,因此无需复制任何代码。然后command也可以调用va_list版本,一切都应该有效。

答案 2 :(得分:0)

va_start(实际上是一个宏)的返回值是指向堆栈上元素的指针,因此是堆栈上某些内容的地址。如果将此参数作为参数传递给另一个函数,则不会传递堆栈值,而是指向堆栈上的值的指针。

为了使这个工作,command函数应该使用va_arg来获取实际的堆栈值并将其传递给marshal_size函数,但是如果你不知道它的类型,就不能这样做堆栈上的值。在这种情况下,将ap作为va_list参数传递并更改marshall_size的参数以获取va_list参数并继续在那里进行堆栈值处理。

答案 3 :(得分:0)

正如其他答案所指出的,如果你想从一个函数“传递”变量参数到另一个函数,你需要编写你的函数来采用一个明确的va_list参数。这很简单:您只需将marshal_size()函数重写为vmarshal_size(),其中va_listmarshal_size(),然后将static uint32_t marshal_size(const char *format, ...) { va_list ap; uint32_t retval; va_start(ap, format); retval = vmarshal_size(format, ap); va_end(ap); return retval; } static uint32_t vmarshal_size(const char *format, va_list ap) { uint32_t retv = 0; uint8_t ub; uint16_t uw; uint32_t ul; char *s; if (!format || !strlen(format)) return 0; d("format = %s \n", format); /* No va_start() */ for (; '\0' != *format; format++) { d("*format = %c \n", *format); if ('%' == *format) { format++; if ('u' == *format) format++; } else { d("FORMAT ERROR\n"); continue; } d("*format = %c \n", *format); switch (*format) { case 's': s = va_arg(ap, char*); d("va_arg = %s\n", (s ? s : "NULL")); if (s) retv += strlen(s) + 1; break; case 'l': ul = va_arg(ap, uint32_t); retv += sizeof(uint32_t); break; case 'w': uw = (uint16_t)va_arg(ap, int); retv += sizeof(uint16_t); break; case 'b': ub = (uint8_t)va_arg(ap, int); retv += sizeof(uint8_t); break; default: goto exit; } continue; exit: break; } /* No va_end() */ return retv; } 本身转换为包装器:

command()

您的vmarshal_size()功能可以同样的方式拨打command()(虽然我注意到您的marshal_size()功能与新{{1}}功能完全相同,可能是您实际上我想做点不同的事情。)