【问题标题】:How to remove this warning: second parameter of ‘va_start’ not last named argument?如何删除此警告:“va_start”的第二个参数不是最后一个命名参数?
【发布时间】:2012-11-02 04:35:13
【问题描述】:

我有一个发出以下警告的函数(见下文):

‘va_start’的第二个参数不是最后命名的参数

这是什么意思以及如何删除它?

函数如下:

static int  ui_show_warning(GtkWindow *parent, const gchar *fmt, size_t size, ...)
    {
      GtkWidget *dialog = NULL;
      va_list args = NULL;
      int count = -1;
      char *msg = NULL;

      if((msg = malloc(size + 1)) == NULL)
        return -12;

      va_start(args, fmt);

      if((count = snprintf(msg, size, fmt, args)) < 0)
        goto outer;

      dialog = gtk_message_dialog_new(parent,
                      GTK_DIALOG_DESTROY_WITH_PARENT,
                      GTK_MESSAGE_WARNING,
                      GTK_BUTTONS_OK,
                      "%s", msg);
      (void) gtk_dialog_run(GTK_DIALOG(dialog));

      gtk_widget_destroy(dialog);

     outer: {
        if(args != NULL)
          va_end(args);

        if(msg != NULL)
          free(msg);

        return count;
      }
    }

【问题讨论】:

标签: c linux gcc


【解决方案1】:

您需要使用size 而不是fmt

va_start(args, size);

它是size,而不是fmt,是最后一个有明确名称的参数(与没有名称的可变参数相反)。您需要将最后一个命名参数传递给va_start,以便它找出可变参数参数在内存中的起始地址。

【讨论】:

  • 我仍然对答案感到困惑。什么是大小?我有一个这样的函数:void function(int, const string& fmt, ...);,当我使用 va_start 时,会显示此警告。那我需要做什么?
  • @TrungNguyen 他的参数列表是parent, fmt, size...... 之前的最后一个命名参数是 size
  • 我已经找到了我的功能的问题。因为我使用了 const string& fmt,所以当我将 fmt 传递给 va_start 时,我需要像 fmt.c_str() => 警告那样转换为 char*。
  • 如果函数根本没有参数怎么办?喜欢Constructor::call(...)
  • @MDP 没有参数的函数不能有变长参数。
【解决方案2】:
second parameter of ‘va_start’ not last named argument

这是什么意思以及如何删除它?

您的函数具有命名参数parentfmtsize。 C 规范说您必须始终将最后一个命名参数传递给va_start,以便与旧编译器兼容。所以你必须通过size,而不是fmt

(但是使用现代编译器,它可能仍然可以工作)

【讨论】:

  • 现代编译器在这种情况下会做什么?例如,它会使用指定的参数,还是本质上将其更改为函数签名中最后一个命名的参数?
  • 现代编译器可能会在本质上将其更改为函数签名中最后一个命名的编译器。现代编译器将此作为内置实现,因此它们可以更好地优化。例如。最后命名的参数可以在寄存器中传递,甚至可以完全优化。较旧的编译器知道参数是按顺序压入堆栈的,因此他们在头文件中将 va_start 等实现为宏,而没有特殊的编译器支持。在这种情况下,它们需要从最后一个命名参数的地址开始。
  • (但不要依赖于此!传递正确的值!)
  • 让我们明确一点:如果您编写的代码调用 va_start 并传递了一些不是最后命名的参数,那么您的代码就有错误。允许编译器将其编译为对 _exit() 的调用,或作为格式化硬盘的指令。更有可能的是,如果您有 if{}else{} 分支执行此操作,则允许编译器假定该分支永远不会发生并删除 if 语句,只留下 else{} 中的代码。它可能碰巧在一个特定系统上的一个特定版本的编译器上执行您想要的操作,但是“错误修复”编译器版本可能会改变它
  • 您依赖于“未定义的行为”。见:stackoverflow.com/questions/2397984/…
【解决方案3】:

我认为这里有一个混淆:大多数人只处理具有格式和可变参数的类似 prinf 的函数。他们认为他们必须传递描述格式的参数名称。但是 va_start 与任何类型的 printf 格式无关。这只是一个计算未命名参数开始的堆栈上的偏移量的函数。

【讨论】:

    【解决方案4】:

    我在Ubuntu20.04上遇到同样的问题,与点赞最多的答案相反, 开头的代码,

    void sprintf(char *str, char *fmt, ...) {
        va_list list;
        int i, len;
        va_start(list, 2);
        ...
    }
    

    然后,代码如下

    void sprintf(char *str, char *fmt, ...) {
        va_list list;
        int i, len;
        va_start(list, fmt);
        ...
    }
    

    问题已解决。

    【讨论】:

    • 那是另一个问题。您甚至根本没有使用参数名称。您的解决方案与现有答案并不矛盾。就像在接受的答案中一样,您需要使用列表中的最后一个参数。
    • @Gerhardh wow,我很困惑,相同的代码在 Deepin 上运行良好,但在 Ubuntu20.04 上却无法运行
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-08
    • 2019-03-06
    • 1970-01-01
    相关资源
    最近更新 更多