va_arg()

Discussion in 'C Programming' started by LIT, Oct 26, 2006.

  1. LIT

    LIT Guest

    Hello,

    I have a question about va_arg() usage :
    Is there any way how I can check or ensure at compile time that
    va_arg() is not called when there are
    no next arguments anymore.
    I understood that calling va_arg() when there are no next arguments
    causes an undefined behaviour.

    Thanks
     
    LIT, Oct 26, 2006
    #1
    1. Advertising

  2. In article <>,
    LIT <> wrote:

    >Is there any way how I can check or ensure at compile time that
    >va_arg() is not called when there are
    >no next arguments anymore.


    No, your program has to work that out for itself. Typically one
    of the arguments indicates how many other arguments there are (as
    in printf()) or a special value (such as 0) is used as an extra
    terminating argument.

    -- Richard
     
    Richard Tobin, Oct 26, 2006
    #2
    1. Advertising

  3. LIT

    Guest

    LIT wrote:

    > I have a question about va_arg() usage :
    > Is there any way how I can check or ensure at compile time that
    > va_arg() is not called when there are
    > no next arguments anymore.


    No - it is essential that you have a means (usually based on your first
    argument) to determine how many times to invoke va_arg().

    See (for example)
    http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
    and
    http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html

    > I understood that calling va_arg() when there are no next arguments
    > causes an undefined behaviour.


    Yep. (My manual page talks of "random errors" :)
     
    , Oct 26, 2006
    #3
  4. LIT

    LIT Guest

    schreef:

    > LIT wrote:
    >
    > > I have a question about va_arg() usage :
    > > Is there any way how I can check or ensure at compile time that
    > > va_arg() is not called when there are
    > > no next arguments anymore.

    >
    > No - it is essential that you have a means (usually based on your first
    > argument) to determine how many times to invoke va_arg().
    >
    > See (for example)
    > http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
    > and
    > http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html
    >
    > > I understood that calling va_arg() when there are no next arguments
    > > causes an undefined behaviour.

    >
    > Yep. (My manual page talks of "random errors" :)


    I understand
    Nevertheless, if I have a function like this :

    foo(unsigned char Nvars, ...)
    {
    unsigned int i;
    unsigned int test[10];
    va_list tag ;
    va_start(tag,Nvars);
    for (i=0 ; i<Nvars ; i++)
    {
    test = va_arg(tag, unsigned int);
    }
    va_end(tag)
    }

    and I do this (eg. accidentally, during coding) :

    foo(4, x, y) ; (x and y are unsigned int params),

    then there is no clear means to detect this at compile time
     
    LIT, Oct 26, 2006
    #4
  5. LIT wrote:
    > schreef:
    >
    > > LIT wrote:
    > >
    > > > I have a question about va_arg() usage :
    > > > Is there any way how I can check or ensure at compile time that
    > > > va_arg() is not called when there are
    > > > no next arguments anymore.

    > >
    > > No - it is essential that you have a means (usually based on your first
    > > argument) to determine how many times to invoke va_arg().
    > >
    > > See (for example)
    > > http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
    > > and
    > > http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html
    > >
    > > > I understood that calling va_arg() when there are no next arguments
    > > > causes an undefined behaviour.

    > >
    > > Yep. (My manual page talks of "random errors" :)

    >
    > I understand
    > Nevertheless, if I have a function like this :
    >
    > foo(unsigned char Nvars, ...)
    > {
    > unsigned int i;
    > unsigned int test[10];
    > va_list tag ;
    > va_start(tag,Nvars);
    > for (i=0 ; i<Nvars ; i++)
    > {
    > test = va_arg(tag, unsigned int);
    > }
    > va_end(tag)
    > }
    >
    > and I do this (eg. accidentally, during coding) :
    >
    > foo(4, x, y) ; (x and y are unsigned int params),
    >
    > then there is no clear means to detect this at compile time


    When we had this issue, I wrote a perl script that scanned our source
    files to ensure that all calls to a predefined set of varargs functions
    had a NULL as the last argument, that being what we used as a sentinel.
    It wasn't perfect, but it was helpful. You might be able to come up
    with a similar tool that meets your needs.

    -David
     
    David Resnick, Oct 26, 2006
    #5
  6. LIT

    Guest

    LIT wrote:

    > if I have a function like this :
    >
    > foo(unsigned char Nvars, ...)


    > and I do this (eg. accidentally, during coding) :
    >
    > foo(4, x, y) ; (x and y are unsigned int params),
    >
    > then there is no clear means to detect this at compile time


    That's true - because the compiler doesn't know (and the language
    provides no means of defining) that the first parameter to foo() has
    any specific meaning.

    The foo() implementation and the foo() invocation are independently
    parsed and compiled (they could be in separate source files, you just
    happened to combine declaring and defining foo() in your example).
     
    , Oct 26, 2006
    #6
  7. LIT wrote:
    > schreef:
    >
    >
    >>LIT wrote:
    >>
    >>
    >>>I have a question about va_arg() usage :
    >>>Is there any way how I can check or ensure at compile time that
    >>>va_arg() is not called when there are
    >>>no next arguments anymore.

    >>
    >>No - it is essential that you have a means (usually based on your first
    >>argument) to determine how many times to invoke va_arg().
    >>
    >>See (for example)
    >>http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/s15.html
    >>and
    >>http://docs.mandragor.org/files/Programming_languages/C/clc_faq_en/C-faq/q15.8.html
    >>
    >>
    >>>I understood that calling va_arg() when there are no next arguments
    >>>causes an undefined behaviour.

    >>
    >>Yep. (My manual page talks of "random errors" :)

    >
    >
    > I understand
    > Nevertheless, if I have a function like this :
    >
    > foo(unsigned char Nvars, ...)
    > {
    > unsigned int i;
    > unsigned int test[10];
    > va_list tag ;
    > va_start(tag,Nvars);
    > for (i=0 ; i<Nvars ; i++)
    > {
    > test = va_arg(tag, unsigned int);
    > }
    > va_end(tag)
    > }
    >
    > and I do this (eg. accidentally, during coding) :
    >
    > foo(4, x, y) ; (x and y are unsigned int params),
    >
    > then there is no clear means to detect this at compile time


    You can compute the number of arguments automatically by writing a macro
    with the same name and use the macro PP_NARG I posted on comp.std.c some
    time ago
    (http://groups.google.com.au/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb?hl=en):

    extern void foo(int n, ...);

    #define foo(...) foo(PP_NARG(__VA_ARGS__)-1, __VA_ARGS__)

    foo(x,y) -> foo(2,x,y)

    The case:

    foo() -> foo(0)

    is a bit more complex but possible.

    a+, ld.
     
    Laurent Deniau, Oct 26, 2006
    #7
  8. LIT

    David Tiktin Guest

    On 26 Oct 2006, "LIT" <> wrote:

    > Nevertheless, if I have a function like this :
    >
    > foo(unsigned char Nvars, ...)
    > {
    > unsigned int i;
    > unsigned int test[10];
    > va_list tag ;
    > va_start(tag,Nvars);
    > for (i=0 ; i<Nvars ; i++)
    > {
    > test = va_arg(tag, unsigned int);
    > }
    > va_end(tag)
    > }
    >
    > and I do this (eg. accidentally, during coding) :
    >
    > foo(4, x, y) ; (x and y are unsigned int params),
    >
    > then there is no clear means to detect this at compile time


    I'd love to hear about a clean, portable solution to this, but I don't
    know of one. In the case you give, where the first argument is the
    number of varargs, you could use a macro to help a little:

    #define FOO_ARGS_1(a) 1, (a)
    #define FOO_ARGS_2(a,b) 2, (a), (b)

    etc. I'm not claiming it's pretty, but you should at least get a
    compiler warning if you invoke one of the macros with the wrong number
    of arguments.

    Dave

    --
    D.a.v.i.d T.i.k.t.i.n
    t.i.k.t.i.n [at] a.d.v.a.n.c.e.d.r.e.l.a.y [dot] c.o.m
     
    David Tiktin, Oct 26, 2006
    #8
  9. LIT

    Eric Sosman Guest

    LIT wrote On 10/26/06 11:23,:
    > [...]
    > Nevertheless, if I have a function like this :
    >
    > foo(unsigned char Nvars, ...)
    > {
    > unsigned int i;
    > unsigned int test[10];
    > va_list tag ;
    > va_start(tag,Nvars);
    > for (i=0 ; i<Nvars ; i++)
    > {
    > test = va_arg(tag, unsigned int);
    > }
    > va_end(tag)
    > }
    >
    > and I do this (eg. accidentally, during coding) :
    >
    > foo(4, x, y) ; (x and y are unsigned int params),
    >
    > then there is no clear means to detect this at compile time


    Right, because the compiler doesn't know how you are going
    to figure out the number and types of the optional arguments.
    The scheme you use could be arbitrarily complicated: Maybe the
    number of optional arguments is the square root of the first
    argument, or maybe foo() stops looking for optional arguments
    once their running sum exceeds the value of the first one, or
    maybe the first argument is actually a bunch of bit-fields that
    encode descriptions of the optional arguments, or ...

    Some compilers have been "taught" the rules for variable-
    argument library functions (Standard and otherwise), and can
    do at least a partial validity check at compile time. At least
    one compiler provides a way for you, the programmer, to state
    that your print_in_colors() function is "printf-like" and can
    be checked similarly. But I haven't heard of a compiler that
    allows you to add new checking rules of your own.

    By the way: Elsethread, someone mentioned using NULL as
    a sentinel to mark the end of the list of optional arguments.
    Thought question: Why is this a bad idea, and how could it
    be made better? (Hint: What is the type of the expression
    the NULL macro expands to?)

    --
     
    Eric Sosman, Oct 26, 2006
    #9
  10. Eric Sosman <> wrote:

    > By the way: Elsethread, someone mentioned using NULL as
    > a sentinel to mark the end of the list of optional arguments.
    > Thought question: Why is this a bad idea, and how could it
    > be made better? (Hint: What is the type of the expression
    > the NULL macro expands to?)


    I assume you're talking about the general case - obviously NULL as a
    sentinel could not work for a function such as printf() where the
    type of each argument varies. I fail to see how using a sentinel
    value to terminate a list of arguments *of the same type* is a bad
    idea and I assume that's not what you were getting at.

    --
    C. Benson Manica | I *should* know what I'm talking about - if I
    cbmanica(at)gmail.com | don't, I need to know. Flames welcome.
     
    Christopher Benson-Manica, Oct 26, 2006
    #10
  11. Christopher Benson-Manica wrote:
    > Eric Sosman <> wrote:
    >
    > > By the way: Elsethread, someone mentioned using NULL as
    > > a sentinel to mark the end of the list of optional arguments.
    > > Thought question: Why is this a bad idea, and how could it
    > > be made better? (Hint: What is the type of the expression
    > > the NULL macro expands to?)

    >
    > I assume you're talking about the general case - obviously NULL as a
    > sentinel could not work for a function such as printf() where the
    > type of each argument varies. I fail to see how using a sentinel
    > value to terminate a list of arguments *of the same type* is a bad
    > idea and I assume that's not what you were getting at.
    >


    I think his point is that you need to cast NULL to void* when passing
    to a varargs function because it may be just a naked 0 and int may be
    smaller than void*. I didn't make up the code in question, it
    preceeded me. But fixing it (with a cast or some other approach)
    doesn't seem worthwhile to me as it is unlikely to break any time soon.

    -David
     
    David Resnick, Oct 26, 2006
    #11
  12. Christopher Benson-Manica <> writes:
    > Eric Sosman <> wrote:
    >> By the way: Elsethread, someone mentioned using NULL as
    >> a sentinel to mark the end of the list of optional arguments.
    >> Thought question: Why is this a bad idea, and how could it
    >> be made better? (Hint: What is the type of the expression
    >> the NULL macro expands to?)

    >
    > I assume you're talking about the general case - obviously NULL as a
    > sentinel could not work for a function such as printf() where the
    > type of each argument varies. I fail to see how using a sentinel
    > value to terminate a list of arguments *of the same type* is a bad
    > idea and I assume that's not what you were getting at.


    The point is that we don't *know* the type of the expression the NULL
    macro expands to; it could be either int or void* (there are other
    possibilities).

    "(void*)NULL" is a better sentinel value.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Oct 26, 2006
    #12
  13. "David Resnick" <> writes:
    > Christopher Benson-Manica wrote:
    >> Eric Sosman <> wrote:
    >>
    >> > By the way: Elsethread, someone mentioned using NULL as
    >> > a sentinel to mark the end of the list of optional arguments.
    >> > Thought question: Why is this a bad idea, and how could it
    >> > be made better? (Hint: What is the type of the expression
    >> > the NULL macro expands to?)

    >>
    >> I assume you're talking about the general case - obviously NULL as a
    >> sentinel could not work for a function such as printf() where the
    >> type of each argument varies. I fail to see how using a sentinel
    >> value to terminate a list of arguments *of the same type* is a bad
    >> idea and I assume that's not what you were getting at.

    >
    > I think his point is that you need to cast NULL to void* when passing
    > to a varargs function because it may be just a naked 0 and int may be
    > smaller than void*. I didn't make up the code in question, it
    > preceeded me. But fixing it (with a cast or some other approach)
    > doesn't seem worthwhile to me as it is unlikely to break any time soon.


    What do you mean by "any time soon"? It could break the moment you
    recompile it on a 64-bit platform.

    You don't actually have to wait for the code to fail before you fix
    it.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Oct 26, 2006
    #13
  14. LIT

    Eric Sosman Guest

    Keith Thompson wrote On 10/26/06 14:55,:
    > Christopher Benson-Manica <> writes:
    >
    >>Eric Sosman <> wrote:
    >>
    >>> By the way: Elsethread, someone mentioned using NULL as
    >>>a sentinel to mark the end of the list of optional arguments.
    >>>Thought question: Why is this a bad idea, and how could it
    >>>be made better? (Hint: What is the type of the expression
    >>>the NULL macro expands to?)

    >>
    >>I assume you're talking about the general case - obviously NULL as a
    >>sentinel could not work for a function such as printf() where the
    >>type of each argument varies. I fail to see how using a sentinel
    >>value to terminate a list of arguments *of the same type* is a bad
    >>idea and I assume that's not what you were getting at.

    >
    >
    > The point is that we don't *know* the type of the expression the NULL
    > macro expands to; it could be either int or void* (there are other
    > possibilities).
    >
    > "(void*)NULL" is a better sentinel value.


    Right. Or (char*)NULL, or (struct thing*)NULL, or
    whatever pointer type the function expects to retrieve
    with va_arg(). The point I wanted to make is that a
    "bare" NULL makes an unreliable sentinel, because its
    type is implementation-defined and quite likely wrong
    for the purpose.

    --
     
    Eric Sosman, Oct 26, 2006
    #14
  15. Keith Thompson wrote:
    > "David Resnick" <> writes:
    > > Christopher Benson-Manica wrote:
    > >> Eric Sosman <> wrote:
    > >>
    > >> > By the way: Elsethread, someone mentioned using NULL as
    > >> > a sentinel to mark the end of the list of optional arguments.
    > >> > Thought question: Why is this a bad idea, and how could it
    > >> > be made better? (Hint: What is the type of the expression
    > >> > the NULL macro expands to?)
    > >>
    > >> I assume you're talking about the general case - obviously NULL as a
    > >> sentinel could not work for a function such as printf() where the
    > >> type of each argument varies. I fail to see how using a sentinel
    > >> value to terminate a list of arguments *of the same type* is a bad
    > >> idea and I assume that's not what you were getting at.

    > >
    > > I think his point is that you need to cast NULL to void* when passing
    > > to a varargs function because it may be just a naked 0 and int may be
    > > smaller than void*. I didn't make up the code in question, it
    > > preceeded me. But fixing it (with a cast or some other approach)
    > > doesn't seem worthwhile to me as it is unlikely to break any time soon.

    >
    > What do you mean by "any time soon"? It could break the moment you
    > recompile it on a 64-bit platform.
    >


    The code in question is in a rather stable state (past QA/load
    tested/released to customers). Changing a few hundred lines with no
    actual functional defect would clearly not be approved -- and rightly
    so I think. I don't consider fixing this critical, just something to
    keep in mind, particularly for when we migrate to 64 bits, where no
    doubt other code will be destabilized too. With our current headers,
    wouldn't matter even then for our C files, as NULL is defined as
    (void*) 0, but those system headers could change too, and as our
    sources include both C and C++ it will break at least in the C++ files
    as NULL there is 0...

    > You don't actually have to wait for the code to fail before you fix
    > it.


    Depending on the state of a project and the perceived risk of the
    failure, sometimes you do.

    -David
     
    David Resnick, Oct 26, 2006
    #15
  16. "David Resnick" <> writes:
    > Keith Thompson wrote:

    [...]
    >> You don't actually have to wait for the code to fail before you fix
    >> it.

    >
    > Depending on the state of a project and the perceived risk of the
    > failure, sometimes you do.


    Fair enough.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
     
    Keith Thompson, Oct 26, 2006
    #16
  17. Keith Thompson <> wrote:

    > The point is that we don't *know* the type of the expression the NULL
    > macro expands to; it could be either int or void* (there are other
    > possibilities).


    > "(void*)NULL" is a better sentinel value.


    Agreed, I appreciate the clarification.

    --
    C. Benson Manica | I *should* know what I'm talking about - if I
    cbmanica(at)gmail.com | don't, I need to know. Flames welcome.
     
    Christopher Benson-Manica, Oct 27, 2006
    #17
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Suzanne Vogel

    variable num args via 'va_arg'

    Suzanne Vogel, Jul 5, 2003, in forum: C++
    Replies:
    2
    Views:
    452
    flekso
    Jul 5, 2003
  2. Gordon Burditt

    Re: va_arg() question

    Gordon Burditt, Aug 7, 2003, in forum: C Programming
    Replies:
    0
    Views:
    413
    Gordon Burditt
    Aug 7, 2003
  3. Eric Sosman

    Re: va_arg() question

    Eric Sosman, Aug 7, 2003, in forum: C Programming
    Replies:
    1
    Views:
    515
    Kevin Easton
    Aug 8, 2003
  4. Artie Gold

    Re: va_arg() question

    Artie Gold, Aug 7, 2003, in forum: C Programming
    Replies:
    0
    Views:
    431
    Artie Gold
    Aug 7, 2003
  5. Mike Wahler

    Re: va_arg() question

    Mike Wahler, Aug 7, 2003, in forum: C Programming
    Replies:
    1
    Views:
    408
    Dave Thompson
    Aug 11, 2003
Loading...

Share This Page