Creating a variadic function that prints any kind of format of ("c", "f", "i", "s"), but doesn't work

huangapple go评论64阅读模式
英文:

Creating a variadic function that prints any kind of format of ("c", "f", "i", "s"), but doesn't work

问题

I see the issue in your code. The problem lies in the way you're using va_arg within the print_all function. In this function, you are assuming that the arguments passed correspond exactly to the format specifiers in the format string, which is causing the mismatch in the printed values.

To fix this issue, you should modify the print_all function to handle the format specifiers and arguments correctly. Here's an updated version of the print_all function:

#include <stdarg.h>
#include <stdio.h>

typedef struct formatter
{
    char spec;
    void (*print)(va_list);
} fmt;

void print_char(va_list args)
{
    printf("%c", va_arg(args, int));
}

void print_int(va_list args)
{
    printf("%d", va_arg(args, int));
}

void print_float(va_list args)
{
    printf("%f", va_arg(args, double));
}

void print_string(va_list args)
{
    char *spec = va_arg(args, char *);
    if (spec == NULL)
    {
        printf("(nil)");
        return;
    }
    printf("%s", spec);
}

void print_all(const char *const format, ...)
{
    fmt f[] = {
        {'c', print_char},
        {'i', print_int},
        {'f', print_float},
        {'s', print_string},
        {'\0', NULL}};

    int i = 0;
    char *separator = "";
    va_list args;
    va_start(args, format);

    if (format == NULL)
    {
        return;
    }

    while (format[i] != '\0')
    {
        if (format[i] == '%')
        {
            i++;
            int j = 0;
            while (f[j].spec)
            {
                if (f[j].spec == format[i])
                {
                    printf("%s", separator);
                    f[j].print(args);
                    separator = ", ";
                    break;
                }
                j++;
            }
        }
        else
        {
            putchar(format[i]);
        }
        i++;
    }
    printf("\n");
    va_end(args);
}

int main(void)
{
    print_all("ceis", 'B', 3, "stSchool");
    return (0);
}

In this modified print_all function, we added support for parsing format specifiers preceded by '%' in the format string. This way, it correctly associates the format specifiers with the corresponding arguments. The output will be as expected: "B, 3, stSchool".

英文:

I've created a struct, that groups the format character and a pointer to the function which prints according to the formatter.

typedef struct formatter
{
char spec;
void (*print)(va_list);
} fmt;

Then, i've created functions that prints each format.

void print_char(va_list args)
{
printf(&quot;%c&quot;, va_arg(args, int));
}
void print_int(va_list args)
{
printf(&quot;%d&quot;, va_arg(args, int));
}
void print_float(va_list args)
{
printf(&quot;%f&quot;, va_arg(args, double));
}
void print_string(va_list args)
{
char *spec = va_arg(args, char *);
if (spec == NULL)
{
printf(&quot;(nil)&quot;);
return;
}
printf(&quot;%s&quot;, spec);
}

in the main variadic function i've created an array of the struct in order to loop over it.

void print_all(const char *const format, ...)
{
fmt f[] = {
{&#39;c&#39;, print_char},
{&#39;i&#39;, print_int},
{&#39;f&#39;, print_float},
{&#39;s&#39;, print_string},
{&#39;\0&#39;, NULL}};
int i = 0, j;
char *separator = &quot;&quot;;
va_list args;
va_start(args, format);
if (format == NULL)
{
return;
}
while (format[i] != &#39;\0&#39;)
{
j = 0;
while (f[j].spec)
{
if (f[j].spec == format[i])
{
printf(&quot;%s&quot;, separator);
f[j].print(args);
separator = &quot;, &quot;;
break;
}
j++;
}
i++;
}
printf(&quot;\n&quot;);
va_end(args);
}

The problem came, when i compile the program and test the case below:

int main(void)
{
print_all(&quot;ceis&quot;, &#39;B&#39;, 3, &quot;stSchool&quot;);
return (0);
}

It prints
B, 66,
Instead of printing
B, 3, stSchool

I need to know where the problem at.

I expect the problem becomes in va_arg in each function, but in order of the less detailed knowledge that i've of variadic functions, i can't change something.
and also i don't wanna implement it with the use of switch cases, in order to modularize my program.

答案1

得分: 4

如果您将一个va_list参数传递给调用va_arg的函数,那么调用函数不再允许使用该va_list(除了调用va_end)。您违反了这个规则,因为print_all调用其他调用va_arg的函数。

修复这个问题的一种方法是将所有的va_arg调用放在同一个函数中。但这与您的函数指针方法不太适用。

另一种修复方法是将va_list *传递给辅助函数,而不是va_list。这在标准中是明确允许的,然后您可以在其他函数中继续使用相同的va_list。因此,您需要修改所有的辅助函数(以及函数指针类型),以接受指针。

C标准参考(C17 7.16/3):

对象ap(类型为va_list)可以作为参数传递给另一个函数;如果该函数调用va_arg宏并以参数ap调用,那么调用函数中ap的值是不确定的,应在对ap进行任何进一步引用之前传递给va_end宏。257

脚注 257:

允许创建一个指向va_list的指针,并将该指针传递给另一个函数,在这种情况下,原始函数可以在另一个函数返回后继续使用原始列表。

英文:

If you pass a va_list parameter to a function that calls va_arg, the calling function is no longer allowed to use that va_list (other than calling va_end). You violate this rule because print_all calls other functions that call va_arg.

One way to fix this would be to put all the va_arg calls in the same function. But this wouldn't work well with your function pointer approach.

Another way to fix it is to pass va_list * to the helper functions instead of va_list. This is explicitly allowed by the standard, and you can then keep using the same va_list in other functions. So you'd need to modify all your helper functions (and the function pointer type) to take a pointer.

C standard reference (C17 7.16/3):

> The object ap [of type va_list] may be passed as an argument to another function; if that function invokes the va_arg macro
with parameter ap, the value of ap in the calling function is indeterminate and shall be passed to the
va_end macro prior to any further reference to ap.<sup>257</sup>

Footnote 257:

> It is permitted to create a pointer to a va_list and pass that pointer to another function, in which case the original
function may make further use of the original list after the other function returns.

答案2

得分: 3

传递 va_list 到多个函数时,通过值传递是无效的。你必须传递一个指向 va_list 的指针。

#include <stdio.h>
#include <stdarg.h>

typedef struct formatter {
  char spec;
  void (*print)(va_list*);
}

void print_char(va_list *args) {
  printf("%c", va_arg(*args, int));
}

void print_int(va_list *args) {
  printf("%d", va_arg(*args, int));
}

void print_float(va_list *args) {
  printf("%f", va_arg(*args, double));
}

void print_string(va_list *args) {
  char *spec = va_arg(*args, char *);
  if (spec == NULL) {
    printf("(nil)");
    return;
  }
  printf("%s", spec);
}

void print_all(const char *const format, ...) {
  fmt f[] = {
      {'c', print_char},
      {'i', print_int},
      {'f', print_float},
      {'s', print_string},
      {'\0', NULL}};
  const char *separator = "";
  va_list args;
  va_start(args, format);

  if (format == NULL) {
    return;
  }

  for (int i = 0; format[i] != '\0'; ++i) {
    for (int j = 0; f[j].spec; f++) {
      if (f[j].spec == format[i]) {
        printf("%s", separator);
        f[j].print(&args);
        separator = ", ";
        break;
      }
    }
  }
  printf("\n");
  va_end(args);
}

int main(void) {
  print_all("cis", 'B', 3, "stSchool");
}
英文:

Passing va_list by value to multiple function is invalid. You have to pass a pointer to va_list.

#include &lt;stdio.h&gt;
#include &lt;stdarg.h&gt;
typedef struct formatter {
char spec;
void (*print)(va_list*);
} fmt;
void print_char(va_list *args) {
printf(&quot;%c&quot;, va_arg(*args, int));
}
void print_int(va_list *args) {
printf(&quot;%d&quot;, va_arg(*args, int));
}
void print_float(va_list *args) {
printf(&quot;%f&quot;, va_arg(*args, double));
}
void print_string(va_list *args) {
char *spec = va_arg(*args, char *);
if (spec == NULL) {
printf(&quot;(nil)&quot;);
return;
}
printf(&quot;%s&quot;, spec);
}
void print_all(const char *const format, ...) {
fmt f[] = {
{&#39;c&#39;, print_char},
{&#39;i&#39;, print_int},
{&#39;f&#39;, print_float},
{&#39;s&#39;, print_string},
{&#39;\0&#39;, NULL}};
const char *separator = &quot;&quot;;
va_list args;
va_start(args, format);
if (format == NULL) {
return;
}
for (int i = 0; format[i] != &#39;\0&#39;; ++i) {
for (int j = 0; f[j].spec; f++) {
if (f[j].spec == format[i]) {
printf(&quot;%s&quot;, separator);
f[j].print(&amp;args);
separator = &quot;, &quot;;
break;
}
}
}
printf(&quot;\n&quot;);
va_end(args);
}
int main(void) {
print_all(&quot;cis&quot;, &#39;B&#39;, 3, &quot;stSchool&quot;);
}

huangapple
  • 本文由 发表于 2023年6月26日 01:27:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76551641.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定