从 C 中的可变参数函数的参数确定类型 [英] Determine types from a variadic function's arguments in C

查看:179
本文介绍了从 C 中的可变参数函数的参数确定类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个关于如何解析可变参数函数的参数的分步说明这样当调用 va_arg(ap, TYPE);我传递了所传递参数的正确数据类型.

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

链接1链接2link3 是我认为是我正在寻找的内容的一部分的示例.我了解了 typedef、struct、enum 和 union 的基本功能,但无法弄清楚一些实际的应用案例,例如链接中的示例.

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

这是我到目前为止所得到的:

#include #include #include #include "my.h"typedef struct s_flist{字符 c;(*F)();} t_flist;int my_printf(char *format, ...){va_list ap;国际我;国际j;整数结果;int arg_count;字符 *cur_arg = 格式;字符 * 类型;t_flist flist[] ={{ 's', &my_putstr },{ 'i', &my_put_nbr },{ 'd', &my_put_nbr }};我 = 0;结果 = 0;类型 = (char*)malloc( sizeof(*format) * (my_strlen(format)/2 + 1) );fparser(类型,格式);arg_count = my_strlen(类型);而(格式[i]){如果(格式 [i] == '%' && 格式 [i + 1]){我++;如果(格式[i] == '%')结果 += my_putchar(format[i]);别的{j = 0;va_start(AP,格式);而 (flist[j].c){如果(格式[i] == flist[j].c)结果 += flist[i].f(va_arg(ap, flist[i].DATA_TYPE??));j++;}}}结果 += my_putchar(format[i]);我++;}va_end(ap);返回(结果);}char *fparser(char *types, char *str){国际我;国际 j;我 = 0;j = 0;而 (str[i]){如果 (str[i] == '%' && str[i + 1] &&str[i + 1] != '%' &&str[i + 1] != ' '){我++;类型[j] = str[i];j++;}我++;}类型[j] = '\0';返回(类型);}

解决方案

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

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

<小时>

编辑添加:

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

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

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

typedef union {国际我;字符 *s;} PRINTABLE_THING;int print_integer(PRINTABLE_THING pt) {//格式化并打印 pt.i}int print_string(PRINTABLE_THING pt) {//格式化并打印pt.s}

通过使用显式的 intchar* 参数,这两个专门的函数可以独立工作;我们创建联合的原因是为了让函数能够正式地采用相同类型的参数,以便它们具有相同的签名,以便我们可以定义一个类型,表示指向该类型函数的指针:

typedef int (*print_printable_thing)(PRINTABLE_THING);

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

typedef struct {字符格式_字符;print_printable_thingprinting_function;FORMAT_CHAR_AND_PRINTING_FUNCTION_PAIRING;FORMAT_CHAR_AND_PRINTING_FUNCTION_PAIRING 格式化程序[] = {{ 'd', 打印整数 },{ 's', 打印字符串 }};int formatter_count = sizeof(formatters)/sizeof(FORMAT_CHAR_AND_PRINTING_FUNCTION_PAIRING);

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

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

for (int i = 0; i 

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

I'd like a step by step explanation on how to parse the arguments of a variadic function so that when calling va_arg(ap, TYPE); I pass the correct data TYPE of the argument being passed.

Currently I'm trying to code printf. I am only looking for an explanation preferably with simple examples but not the solution to printf since I want to solve this personal challenge myself to progress.

Link1, link2 and link3 are examples which I think are part of what I'm looking. I the basics of what a typedef, struct, enum and union do but can't figure out some practical application case like the examples in the links.

What do they really mean ? I can't wrap my brain around how they work. How can I pass the data type from a union to va_arg like in the links examples ? How does it match ? with a modifier like %d, %i ... or the data type of a parameter ?

Here's what I've got so far:

#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);
}

解决方案

You can't get actual type information from va_list. You can get what you're looking for from format. What it seems you're not expecting is: none of the arguments know what the actual types are, but format represents the caller's idea of what the types should be. (Perhaps a further hint: what would the actual printf do if a caller gave it format specifiers that didn't match the varargs passed in? Would it notice?)

Your code would have to parse the format string for "%" format specifiers, and use those specifiers to branch into reading the va_list with specific hardcoded types. For example, (pseudocode) if (fspec was "%s") { char* str = va_arg(ap, char*); print out str; }. Not giving more detail because you explicitly said you didn't want a complete solution.


Edited to add:

You will never have a type as a piece of runtime data that you can pass to va_arg as a value. The second argument to va_arg must be a literal, hardcoded specification referring to a known type at compile time. (Note that va_arg is a macro that gets expanded at compile time, not a function that gets executed at runtime - you couldn't have a function taking a type as an argument.)

A couple of your links suggest keeping track of types via an enum, but this is only for the benefit of your own code being able to branch based on that information; it is still not something that can be passed to va_arg. You have to have separate pieces of code saying literally va_arg(ap, int) and va_arg(ap, char*) so there's no way to avoid a switch or a chain of ifs.

The solution you want to make, using the unions and structs, would start from something like this:

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
}

The two specialized functions would work fine on their own by taking explicit int or char* params; the reason we make the union is to enable the functions to formally take the same type of parameter, so that they have the same signature, so that we can define a single type that means pointer to that kind of function:

typedef int (*print_printable_thing)(PRINTABLE_THING);

Now your code can have an array of function pointers of type print_printable_thing, or an array of structs that have print_printable_thing as one of the structs' fields:

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);

(Yes, the names are all intentionally super verbose. You'd probably want shorter ones in the real program, or even anonymous types where appropriate.)

Now you can use that array to select the correct formatter at runtime:

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

But the process of getting the correct thing into current_printable_thing is still going to involve branching to get to a va_arg(ap, ...) with the correct hardcoded type. Once you've written it, you may find yourself deciding that you didn't actually need the union nor the array of structs.

这篇关于从 C 中的可变参数函数的参数确定类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆