簡體   English   中英

這個遞歸的va_arg代碼有什么問題?

[英]What is wrong with this recursive va_arg code?

我正在嘗試使用變量參數列表創建泛型函數。 設計的一部分是這些功能中的一些相互調用。 不幸的是它似乎不起作用。 如您所見,如果您運行下面的簡單代碼,對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;
}

你必須使marshal_size采用va_list而不是... 請參閱c-faq.com常見問題解答中的問題15.12

我認為你不能將va_list傳遞給帶有變量參數的函數。 我相信你需要另一個版本的marshal_size ,它將va_list作為一個硬參數。

關於這一切在底層如何運作的細節,我有點生疏了。 所以我不會嘗試解釋。 但作為支持,我建議這就是我們擁有此類函數的vprintf,vfprintf和vsprintf版本的原因。

很可能你可以使marshal_size(const char *format, ...)調用marshal_size(const char *format, va_list arg_ptr)來實際執行其功能,因此不必復制任何代碼。 然后command也可以調用va_list版本,一切都應該工作。

va_start(實際上是一個宏)的返回值是指向堆棧上元素的指針,因此是堆棧上某些內容的地址。 如果將此參數作為參數傳遞給另一個函數,則不會傳遞堆棧值,而是傳遞堆棧上的值的指針。

為了使其工作, command函數應使用va_arg來獲取實際的堆棧值並將其傳遞給marshal_size函數,但是如果您不知道堆棧上的值的類型,則無法執行此操作。 在這種情況下,將ap作為va_list參數傳遞並更改marshall_size的參數以獲取va_list參數並繼續在那里進行堆棧值處理。

正如其他答案所指出的那樣,如果要將變量參數從一個函數“傳遞”到另一個函數,則需要編寫函數以獲取顯式的va_list參數。 這很簡單:你只需要將marshal_size()函數重寫為vmarshal_size() va_list marshal_size() ,然后將marshal_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()完全相同,可能你實際上想要做一些不同的事情)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM