使用varargs在我的printf包装器中没有返回错误

时间:2014-02-19 21:04:41

标签: c variadic-functions stdio

我最近重新开始使用C,我决定编写一个库作为stdio.h的包装器。目标是尽可能地进行所有错误检查,以便用户在调用stdio函数时不必自己完成。这部分是为了学习,部分是为了实际使用(因为我经常使用stdio)。

当我编写以下内容(在main中)时,gcc在编译时会出错,因为应该有一个整数作为另一个参数但没有传递。

printf("Your integer: %d\n");

如果它有用,这是我的编译器标志:

-std=c11 -pedantic-errors -Wall -Wextra -Werror

这是我当前包装函数的一部分。它完美地工作,并在传递有效/正确的参数时检查相当多的错误:

uintmax_t gsh_printf(const char *format, ...)
{
    va_list arg;
    int cnt;
    va_start(arg, format);
    cnt = vprintf(format, arg);
    va_end(arg);
    // Analyze cnt and check for stream errors here
    return (uintmax_t)cnt;
}

但如果我打电话,问题就在这里:

gsh_printf("Your integer: %d\n");

给出错误,它甚至运行!通常的输出类似于:

Your integer: 1799713

或者其他一些数字,暗示它正在访问未分配给它的内存,但它也不会给出分段错误。

那么,为什么它不会出现任何错误?我怎样才能编写自己的代码,以便在检查类型,数量等等之后出现编译时错误,或者至少是运行时错误?

当然,非常感谢任何帮助,如果您需要更多信息,请告诉我。谢谢!

2 个答案:

答案 0 :(得分:4)

对于fprintffscanf个函数系列,如果缺少转换规范对应的参数,则函数调用将调用未定义的行为。

使用gcc使用格式(archetype,string-index,first-to-check)函数属性来请求诊断:

extern uintmax_t gsh_printf(const char *format, ...)
    __attribute__ ((format (printf, 1, 2)));

uintmax_t gsh_printf(const char *format, ...)
{
    va_list arg;
    int cnt;
    va_start(arg, format);
    cnt = vprintf(format, arg);
    va_end(arg);
    // Analyze cnt and check for stream errors here
    return (uintmax_t)cnt;
}

有关 archetype string-index 首先检查的说明,请参阅文档:

http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

例如,使用上面的示例,使用-Wall(因为-Wformat),使用以下语句:

gsh_printf("Your integer: %d\n");

你会收到这个警告:

  

警告:格式'%d'需要匹配的'int'参数[-Wformat]

使用-Wall(因为-Wformat-extra-args),您还会收到额外参数的警告:

gsh_printf("Your integer: %d\n", 0, 1);

给出

  

警告:格式[-Wformat-extra-args]

的参数太多

答案 1 :(得分:0)

在C中,无法确定使用va_list时传递的参数数量是否是所需参数的数量。在C调用约定中,参数从最右边的参数开始被压入堆栈。 printf的工作方式是解析格式字符串,并在需要时从堆栈中弹出一个值。因此,在调用

时,您会得到随机数

gsh_printf("Your integer: %d\n");

您需要事先知道提供了多少使用va_list无法完成的参数。

您可以通过使用某种容器类来保存所有参数并使用容器中的元素数来检查是否有足够的数据来解决这个问题。

另请注意,'args'只是指向参数列表开头的指针。因此,当您将其传递给vprintf时,vprintf只会打印指针的值。