【问题标题】:Determine types from a variadic function's arguments in C从 C 中的可变参数函数的参数确定类型
【发布时间】:2014-12-14 23:51:03
【问题描述】:

我想逐步解释如何解析可变参数函数的参数 这样当调用 va_arg(ap, TYPE);我传递了正在传递的参数的正确数据类型。

目前我正在尝试编写 printf 代码。 我只是寻找解释,最好用简单的例子,但不是 printf 的解决方案,因为我想自己解决这个个人挑战以取得进步。

Link1link2link3 是我认为是我正在寻找的示例的一部分。 我了解 typedef、struct、enum 和 union 的基本功能,但无法弄清楚链接中的示例等实际应用案例。

它们的真正含义是什么?我无法想象它们是如何工作的。 如何像链接示例中那样将数据类型从联合传递到 va_arg ?它是如何匹配的? 使用 %d、%i ... 之类的修饰符或参数的数据类型?

这是我目前得到的:

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "my.h"

typedef struct s_flist
{
    char c;
    (*f)();
}              t_flist;

int     my_printf(char *format, ...)
{
    va_list ap;
    int     i;
    int     j;
    int     result;
    int     arg_count;
    char    *cur_arg = format;
    char    *types;
    t_flist flist[] = 
    {
        { 's',  &my_putstr  },
        { 'i',  &my_put_nbr },
        { 'd',  &my_put_nbr }
    };

    i = 0;
    result = 0;
    types = (char*)malloc( sizeof(*format) * (my_strlen(format) / 2 + 1) );
    fparser(types, format);
    arg_count = my_strlen(types);

    while (format[i])
    {
        if (format[i] == '%' && format[i + 1])
        {
            i++;
            if (format[i] == '%')
                result += my_putchar(format[i]);
            else
            {
                j = 0;
                va_start(ap, format);
                while (flist[j].c)
                {
                    if (format[i] == flist[j].c)
                        result += flist[i].f(va_arg(ap, flist[i].DATA_TYPE??));
                    j++;
                }
            }
        }
        result += my_putchar(format[i]);
        i++;
    }

    va_end(ap);
    return (result);
}

char    *fparser(char *types, char *str)
{
    int     i;
    int     j;

    i = 0;
    j = 0;
    while (str[i])
    {
        if (str[i] == '%' && str[i + 1] &&
            str[i + 1] != '%' && str[i + 1] != ' ')
        {
            i++;
            types[j] = str[i];
            j++;
        }
        i++;
    }
    types[j] = '\0';
    return (types);
}

【问题讨论】:

  • 是的 ft_printf

标签: printf variadic-functions


【解决方案1】:

您无法从va_list 获取实际类型信息。你可以从format 得到你想要的东西。您似乎没有预料到的是:没有一个参数知道实际类型是什么,但format 代表调用者对类型应该的想法。 (也许进一步的提示:如果调用者给它的格式说明符与传入的可变参数不匹配,实际 printf 会做什么?它会注意到吗?)

您的代码必须解析“%”格式说明符的格式字符串,并使用这些说明符分支以读取具有特定硬编码类型的va_list。例如,(伪代码)if (fspec was "%s") { char* str = va_arg(ap, char*); print out str; }。没有提供更多细节,因为您明确表示您不想要完整的解决方案。


编辑添加:

您永远不会有一个 type 作为运行时数据,您可以将其作为值传递给va_argva_arg 的第二个参数必须是在编译时引用已知类型的文字硬编码规范。 (请注意,va_arg 是在编译时扩展的宏,而不是在运行时执行的函数 - 您不能让函数将类型作为参数。)

您的一些链接建议通过枚举跟踪类型,但这只是为了您自己的代码能够基于该信息进行分支;它仍然不能传递给va_arg。你必须有单独的代码片段,字面意思是va_arg(ap, int)va_arg(ap, char*),所以没有办法避免switchifs 链。

您想要使用联合和结构创建的解决方案将从以下内容开始:

typedef union {
  int i;
  char *s;
} PRINTABLE_THING;

int print_integer(PRINTABLE_THING pt) {
  // format and print pt.i
}
int print_string(PRINTABLE_THING pt) {
  // format and print pt.s
}

这两个专门的函数可以通过显式的intchar* 参数单独工作;我们创建并集的原因是为了使函数能够正式采用相同类型的参数,以便它们具有相同的签名,这样我们就可以定义一个单一的类型来表示指向那种函数的指针

typedef int (*print_printable_thing)(PRINTABLE_THING);

现在您的代码可以有一个 print_printable_thing 类型的函数指针数组,或者一个将 print_printable_thing 作为结构字段之一的结构数组:

typedef struct {
  char format_char;
  print_printable_thing printing_function;
} FORMAT_CHAR_AND_PRINTING_FUNCTION_PAIRING;

FORMAT_CHAR_AND_PRINTING_FUNCTION_PAIRING formatters[] = {
  { 'd', print_integer },
  { 's', print_string }
};
int formatter_count = sizeof(formatters) / sizeof(FORMAT_CHAR_AND_PRINTING_FUNCTION_PAIRING);

(是的,这些名称都故意超级冗长。您可能希望在实际程序中使用较短的名称,甚至在适当的情况下使用匿名类型。)

现在您可以使用该数组在运行时选择正确的格式化程序:

for (int i = 0; i < formatter_count; i++)
  if (current_format_char == formatters[i].format_char)
    result += formatters[i].printing_function(current_printable_thing);

但是将正确的东西放入current_printable_thing 的过程仍然需要分支到具有正确硬编码类型的va_arg(ap, ...)。编写完成后,您可能会发现自己实际上并不需要联合或结构数组。

【讨论】:

  • 哦,好吧,我尝试了一个类型错误的说明符,当我尝试编译时会弹出错误。我知道 %s 应该是一个字符串,$d 是一个 int 等等,但是我怎样才能将说明符与类型动态关联到 va_arg 是一个谜。
  • 哦,如果您的编译器能够捕捉到printf("%s", 0.1),那只是因为它很聪明,并且将printf 视为一个特殊名称。通常,这不是在编译时(也不是在运行时)可以捕获的东西。编辑答案以提供另一个提示。
  • 感谢您的回答。我已经完成了您提示我做的所有事情(请参阅帖子上的代码)。我被卡住的实际点是:我知道要通过 %s、%i ...等传递给 va_arg 的类型,我正在寻找如何在没有“if 森林”或 switch/case 的情况下做到这一点使用函数指针数组。可以打印字符、打印字符串、打印数字以及 printf 可以执行的所有其他类型的函数。
  • 我已经弄清楚了如何解析格式字符串并将相应的类型传递给 va_arg,请参阅我在帖子中的代码。我现在正在搜索使用函数指针数组(my_putchar、my_putstr、my_put_number ...)来执行此操作。我正在寻找一种方法来做到这一点。我认为THIS 答案和this one + this last one 是我需要的,但不完全理解包含枚举和联合的结构的使用。我希望你明白我现在在哪里。
  • 一开始没有完全理解你的问题是我的错。再次更新答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-23
  • 2012-09-23
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
相关资源
最近更新 更多