26

I have a function (see below) that is emitting the following warning:

second parameter of ‘va_start’ not last named argument

What does it means and how to remove it?

The function is as the following:

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;
      }
    }
halfer
  • 19,824
  • 17
  • 99
  • 186
Jack
  • 16,276
  • 55
  • 159
  • 284
  • 3
    It means what it says. You need to give it the last parameter before the ellipsis. You might find this helpful for truly explaining why: http://www.codeproject.com/Articles/4181/Variable-Argument-Functions – chris Nov 02 '12 at 04:36

4 Answers4

27

You need to use size instead of fmt:

va_start(args, size);

It is size, not fmt, that is the last parameter that has an explicit name (as opposed to vararg parameters, which have no names). You need to pass the last named parameter to va_start in order for it to figure out the address in memory at which the vararg parameters start.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 2
    I still confuse about the answer. what is size? I have a function like that: void function(int, const string& fmt, ...);, and when I use va_start, this warning is displayed. So what need I do? – Trung Nguyen Sep 26 '16 at 03:08
  • 1
    @TrungNguyen His arg list is `parent, fmt, size`, and `...`. Last named parameter before `...` is `size`. – Sergey Kalinichenko Sep 26 '16 at 06:13
  • 1
    I have figured out the problem with my function. Because I used const string& fmt, when I pass fmt to va_start I need to convert to char* like fmt.c_str() => warning. – Trung Nguyen Sep 26 '16 at 06:37
  • what if the function has no arguments at all? like `Constructor::call(...)` – M D P Jul 23 '18 at 08:26
  • 1
    @MDP Functions with no parameters cannot have variable length arguments. – Sergey Kalinichenko Jul 23 '18 at 08:45
8
second parameter of ‘va_start’ not last named argument

What does it means and how to remove it?

Your function has named parameters parent, fmt and size. The C spec says you have to always pass the last named parameter to va_start, for compatibility with older compilers. So you must pass size, not fmt.

(But with a modern compiler, it might work anyway)

user9876
  • 10,954
  • 6
  • 44
  • 66
  • What will a modern compiler do though in this case? E.g., will it use the parameter specified anyway, or intrinsically change it to be the last named one in the function signature? – S. Kalabukha Jan 20 '22 at 11:30
  • 1
    A modern compiler will probably intrinsically change it to be the last named one in the function signature. Modern compilers implement this as a built-in so they can optimize better. E.g. the last named parameter may be passed in a register or even optimized out completely. Older compilers knew that parameters were pushed onto the stack in order, so they implemented va_start etc as macros in the header file, without special compiler support. In that case, they needed to start with the address of the last named parameter. – user9876 Jan 21 '22 at 17:24
  • 1
    (But don't depend on that! Pass the correct value!) – user9876 Jan 21 '22 at 17:24
  • If so (intrinsic change), but the programmer meant exactly what they wrote, it will lead to a bug. We have cross-platform compilation where VS says nothing, but gcc on Linux warns about it. So that may lead to different behavior under different OSes. – S. Kalabukha Jan 25 '22 at 09:03
  • 1
    Lets be clear: If you write code that calls va_start and pass something that is not the last named parameter, then YOUR CODE HAS A BUG. A compiler is allowed to compile that as a call to _exit(), or as instructions that format your hard disk. More likely, if you have an if{}else{} where the if{} branch does this, the compiler is allowed to assume that that branch never happens and remove the if statement, just leaving the code that was in else{}. It may happen to do what you want on one particular version of a compiler on one particular system, but a "bugfix" compiler release might change it – user9876 Feb 18 '22 at 22:41
  • 1
    You are relying on "undefined behaviour". See: https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior – user9876 Feb 18 '22 at 22:46
  • Yes. The programmer that wrote it did not realize that until I showed them this topic here. So the bug is now fixed, thank you. – S. Kalabukha Feb 23 '22 at 16:16
5

I think there is a confusion here: most of people only deal with prinf-like functionsh which have format and varargs. and they think they have to pass parameter name which describes format. however va_start has nothing to do with any kind of printf like format. this is just a function which calculates offset on the stack where unnamed parameters start.

mishmashru
  • 459
  • 5
  • 8
0

I have the same problem on Ubuntu20.04,Contrary to the answer with the most likes, the code at the beginning,

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

and then, the code as follows

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

Problem was sovled.

wangsir
  • 374
  • 2
  • 7
  • That was another issue. You did not even use a parameter name at all. Your solution is not contrary to the existing answer. Exactly as in the accepted answer you need to use the last parameter from the list. – Gerhardh Dec 02 '20 at 09:00
  • @Gerhardh wow,I am so confused that the same code worked well on Deepin, but didn't work on Ubuntu20.04 – wangsir Dec 10 '20 at 13:43