英文:
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("%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);
}
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[] = {
{'c', print_char},
{'i', print_int},
{'f', print_float},
{'s', print_string},
{'\0', NULL}};
int i = 0, j;
char *separator = "";
va_list args;
va_start(args, format);
if (format == NULL)
{
return;
}
while (format[i] != '\0')
{
j = 0;
while (f[j].spec)
{
if (f[j].spec == format[i])
{
printf("%s", separator);
f[j].print(args);
separator = ", ";
break;
}
j++;
}
i++;
}
printf("\n");
va_end(args);
}
The problem came, when i compile the program and test the case below:
int main(void)
{
print_all("ceis", 'B', 3, "stSchool");
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 <stdio.h>
#include <stdarg.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}};
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");
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论