variadic function

Discussion in 'C Programming' started by Quentin Pope, Nov 5, 2011.

  1. Quentin Pope

    Quentin Pope Guest

    Consider the variadic function with the following prototype:
    int foo(int num,...);
    Here 'num' specifies the number of arguments, and assume that all the
    arguments that should be passed to this function are of type int.
    (My question has nothing to do with the definition of the function
    foo, so don't bother about it.)

    If I call the function as:
    foo(2,3,4,5,6,7,8);/*More arguments than expected*/

    Here I call foo with too many arguments than expected by its
    definition.

    Assuming that the prototype is visible in the scope of the function
    call and the definition of function foo itself does NOT produce any
    UB, please tell me if this function call produces UB or not.

    I am not able to find any explicit clause from C99 which deals with
    this behavior.

    Though I find that:

    7.19.6.1 The fprintf function
    The fprintf function writes output to the stream pointed to by stream,
    under control of the string pointed to by format that specifies how
    subsequent arguments are converted for output. If there are
    insufficient arguments for the format, the behavior is undefined. If
    the format is exhausted while arguments remain, the excess arguments
    are evaluated (as always) but are otherwise ignored.

    This is highly specific to fprintf. I wonder if it applies to all
    variadic functions.

    Can I assume that the behavior of this function call is UB by
    ommission?
    Or am I missing something, here?

    QP


    ***

    This email and any file attachments(s) are confidential, may be legally
    privileged, and are intended solely for use by the identified recipient
    (s). If you received the email in error, please notify the sender and
    delete the message and any copies completely from your computer.
    Dissemination, distribution, or copying of this communication, in whole
    or in part, by any unintended or unauthorized recipient is prohibited and
    may subject you to liability under 18 U.S.C. sec. 2511. Jameston
    Solutions, Inc. assumes no liability and makes no warranties or
    representations that the email or any file attachments are totally secure
    and/or virus free. The recipient is advised to check for viruses prior to
    opening any file attached to the email. Statements contained in the email
    or any file attachment may not be authorized or endorsed by Jameston
    Solutions, Inc. Jameston Solutions, Inc. accepts no liability, whether in
    contract, tort, or equity, for any unauthorized statement or offer of
    contract communicated in the email or any file attached by the sender. No
    employee or agent is authorized to conclude any binding agreement on
    behalf of Jameston Solutions, Inc with another party by email without
    express written confirmation

    WARNING: Computer viruses can be transmitted via email. The recipient
    should check this email and any attachments for the presence of viruses.
    The company accepts no liability for any damage caused by any virus
    transmitted by this email. E-mail transmission cannot be guaranteed to be
    secure or error-free as information could be intercepted, corrupted,
    lost, destroyed, arrive late or incomplete, or contain viruses. The
    sender therefore does not accept liability for any errors or omissions in
    the contents of this message, which arise as a result of e-mail
    transmission.

    Warning: Although the company has taken reasonable precautions to ensure
    no viruses are present in this email, the company cannot accept
    responsibility for any loss or damage arising from the use of this email
    or attachments.
    Quentin Pope, Nov 5, 2011
    #1
    1. Advertising

  2. Quentin Pope <> writes:
    > Consider the variadic function with the following prototype:
    > int foo(int num,...);
    > Here 'num' specifies the number of arguments, and assume that all the
    > arguments that should be passed to this function are of type int.
    > (My question has nothing to do with the definition of the function
    > foo, so don't bother about it.)
    >
    > If I call the function as:
    > foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >
    > Here I call foo with too many arguments than expected by its
    > definition.
    >
    > Assuming that the prototype is visible in the scope of the function
    > call and the definition of function foo itself does NOT produce any
    > UB, please tell me if this function call produces UB or not.
    >
    > I am not able to find any explicit clause from C99 which deals with
    > this behavior.
    >
    > Though I find that:
    >
    > 7.19.6.1 The fprintf function
    > The fprintf function writes output to the stream pointed to by stream,
    > under control of the string pointed to by format that specifies how
    > subsequent arguments are converted for output. If there are
    > insufficient arguments for the format, the behavior is undefined. If
    > the format is exhausted while arguments remain, the excess arguments
    > are evaluated (as always) but are otherwise ignored.
    >
    > This is highly specific to fprintf. I wonder if it applies to all
    > variadic functions.


    I believe the behavior is well defined.

    The call itself cannot reasonably have undefined behavior, since foo()
    itself *could* have called va_arg enough times to consume all the
    arguments.

    Inside foo, the only potentially problematic think you're doing is
    invoking va_end() before consuming all the arguments. Invoking va_arg()
    when there are no more arguments is UB (C99 7.15.11.1p), but that
    doesn't apply here.

    There's nothing in the description of va_end() that suggests that its
    behavior depends on whether previous calls to va_arg() have consumed all
    the arguments.

    (The behavior of fprintf() is described explicitly because there's no
    requirement for fprintf() to use the <stdarg.h> mechanism; legacy
    implementations would have used something else, perhaps <varargs.h>,
    and fprintf() could be implemented in non-portable C or in a language
    other than C.)

    [...]
    >
    > This email and any file attachments(s) are confidential, may be legally
    > privileged, and are intended solely for use by the identified recipient

    [29 lines deleted]
    > responsibility for any loss or damage arising from the use of this email
    > or attachments.


    If you can find a way to post without including this nonsense, I'm sure
    we'd all appreciated it.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Nov 5, 2011
    #2
    1. Advertising

  3. Quentin Pope

    James Kuyper Guest

    On 11/05/2011 06:32 PM, Quentin Pope wrote:
    > Consider the variadic function with the following prototype:
    > int foo(int num,...);
    > Here 'num' specifies the number of arguments, and assume that all the
    > arguments that should be passed to this function are of type int.
    > (My question has nothing to do with the definition of the function
    > foo, so don't bother about it.)
    >
    > If I call the function as:
    > foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >
    > Here I call foo with too many arguments than expected by its
    > definition.
    >
    > Assuming that the prototype is visible in the scope of the function
    > call and the definition of function foo itself does NOT produce any
    > UB, please tell me if this function call produces UB or not.
    >
    > I am not able to find any explicit clause from C99 which deals with
    > this behavior.
    >
    > Though I find that:
    >
    > 7.19.6.1 The fprintf function
    > The fprintf function writes output to the stream pointed to by stream,
    > under control of the string pointed to by format that specifies how
    > subsequent arguments are converted for output. If there are
    > insufficient arguments for the format, the behavior is undefined. If
    > the format is exhausted while arguments remain, the excess arguments
    > are evaluated (as always) but are otherwise ignored.
    >
    > This is highly specific to fprintf. I wonder if it applies to all
    > variadic functions.


    Each variadic function must have some mechanism for determining how many
    arguments it's received; there's a wide variety to choose from. However,
    once that mechanism has been used, the same principles apply: if the
    actual number of arguments is larger than the function expects, it won't
    know that they're accessible, and so it shouldn't try to access them.
    Nothing will go bad for any reason other than the direct and indirect
    consequences of the fact that it didn't try to access them.

    > Can I assume that the behavior of this function call is UB by
    > ommission?
    > Or am I missing something, here?


    There is no problem here at the language level. The fact that 'num' is
    supposed to be the count of the number of arguments is known only by the
    implementor of foo(), and those who read the documentation for foo(). In
    particular, the compiler doesn't know anything about how many arguments
    foo() is expecting, and as a result must pass all 6 variable arguments
    in such a way that they could be accessed using the features of
    <stdarg.h>, if foo() decided to try to. It is no error for foo() to fail
    to look at all the arguments that were passed to it.

    The other way around is more problematic: a call to foo(8, 7, 6, 5, 4,
    3, 2) informs foo(), incorrectly, that it has 8 arguments, when in fact
    it only has one fixed argument and 6 variable arguments. If the body of
    foo() makes an attempt to access the argument that should have come
    after 2, then the behavior would be undefined (7.15.1.3p2).
    --
    James Kuyper
    James Kuyper, Nov 5, 2011
    #3
  4. James Kuyper <> writes:
    > On 11/05/2011 06:32 PM, Quentin Pope wrote:
    >> Consider the variadic function with the following prototype:
    >> int foo(int num,...);
    >> Here 'num' specifies the number of arguments, and assume that all the
    >> arguments that should be passed to this function are of type int.
    >> (My question has nothing to do with the definition of the function
    >> foo, so don't bother about it.)
    >>
    >> If I call the function as:
    >> foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >>
    >> Here I call foo with too many arguments than expected by its
    >> definition.
    >>
    >> Assuming that the prototype is visible in the scope of the function
    >> call and the definition of function foo itself does NOT produce any
    >> UB, please tell me if this function call produces UB or not.
    >>
    >> I am not able to find any explicit clause from C99 which deals with
    >> this behavior.
    >>
    >> Though I find that:
    >>
    >> 7.19.6.1 The fprintf function
    >> The fprintf function writes output to the stream pointed to by stream,
    >> under control of the string pointed to by format that specifies how
    >> subsequent arguments are converted for output. If there are
    >> insufficient arguments for the format, the behavior is undefined. If
    >> the format is exhausted while arguments remain, the excess arguments
    >> are evaluated (as always) but are otherwise ignored.
    >>
    >> This is highly specific to fprintf. I wonder if it applies to all
    >> variadic functions.

    >
    > Each variadic function must have some mechanism for determining how many
    > arguments it's received; there's a wide variety to choose from. However,
    > once that mechanism has been used, the same principles apply: if the
    > actual number of arguments is larger than the function expects, it won't
    > know that they're accessible, and so it shouldn't try to access them.
    > Nothing will go bad for any reason other than the direct and indirect
    > consequences of the fact that it didn't try to access them.


    Note that the standard *could* have stated that the behavior of
    invoking va_end() before consuming all the actual arguments with
    va_arg() is undefined. There's even an argument to be made that
    since it doesn't explicitly define the behavior, the behavior
    is therefore undefined. But since it does define the behavior of
    va_end(), and that definition makes no reference to whether va_arg()
    has been used or not, I'd say that the behavior is well defined.

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Nov 5, 2011
    #4
  5. Quentin Pope

    Kaz Kylheku Guest

    On 2011-11-05, Quentin Pope <> wrote:
    > Consider the variadic function with the following prototype:
    > int foo(int num,...);
    > Here 'num' specifies the number of arguments, and assume that all the
    > arguments that should be passed to this function are of type int.
    > (My question has nothing to do with the definition of the function
    > foo, so don't bother about it.)
    >
    > If I call the function as:
    > foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >
    > Here I call foo with too many arguments than expected by its
    > definition.


    There is no way that a definition can express that, though. A variadic
    function does not expect any particular number of parameters (just at least the
    required ones).

    A variadic funtion can fail to extract all the parameters by calling the va_arg
    macro fewer times than there are parameters.

    > insufficient arguments for the format, the behavior is undefined. If
    > the format is exhausted while arguments remain, the excess arguments
    > are evaluated (as always) but are otherwise ignored.


    Exactly.

    > This is highly specific to fprintf. I wonder if it applies to all
    > variadic functions.


    Nothing is written that all arguments must be consumed.

    > Can I assume that the behavior of this function call is UB by
    > ommission?


    No, because there is no omission. Every step can be verified to have
    a defined behavior.

    "UB by omission" occurs in cases when you actually cannot infer
    what the behavior should be.

    The concern about what happens to arguments that are not pulled out
    is a concern about some steps that the program /did not/ execute.

    Look at the steps that the program /did/ execute, and are those defined?

    Sometimes a program can invoke UB by neglecting to take some required steps
    that a situation calls for. That kind of situation has to be spelled out.

    For instance, if an input operation is done on an update stream after an output
    operation, without an intervening fflush, the behavior is undefined. If this
    was not spelled out, the behavior would have to be considered defined (since an
    update stream is one on which you can do a mixture of reads and writes, and if
    no combination is documented as broken, then you can assume it's not broken.)

    > This email and any file attachments(s) are confidential, may be legally
    > privileged, and are intended solely for use by the identified recipient


    The above footer is egregiously excessive and netiquette-violating. I
    understand it's not your fault. If you give me the e-mail address of whoever is
    responsible I will share with them an outsider's perspective. You should not
    have your posting stigmatized by reams of legalese garbage.
    Kaz Kylheku, Nov 6, 2011
    #5
  6. Quentin Pope

    Kaz Kylheku Guest

    On 2011-11-05, Quentin Pope <> wrote:
    > Consider the variadic function with the following prototype:
    > int foo(int num,...);
    > Here 'num' specifies the number of arguments, and assume that all the
    > arguments that should be passed to this function are of type int.
    > (My question has nothing to do with the definition of the function
    > foo, so don't bother about it.)
    >
    > If I call the function as:
    > foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >
    > Here I call foo with too many arguments than expected by its
    > definition.


    There is no way that a definition can express that, though. A variadic
    function does not expect any particular number of parameters (just at least the
    required ones).

    A variadic funtion can fail to extract all the parameters by calling the va_arg
    macro fewer times than there are parameters.

    > insufficient arguments for the format, the behavior is undefined. If
    > the format is exhausted while arguments remain, the excess arguments
    > are evaluated (as always) but are otherwise ignored.


    Exactly.

    > This is highly specific to fprintf. I wonder if it applies to all
    > variadic functions.


    Nothing is written that all arguments must be consumed.

    > Can I assume that the behavior of this function call is UB by
    > ommission?


    No, because there is no omission. Every step can be verified to have
    a defined behavior.

    "UB by omission" occurs in cases when you actually cannot infer
    what the behavior should be.

    The concern about what happens to arguments that are not pulled out
    is a concern about some steps that the program /did not/ execute.

    Look at the steps that the program /did/ execute, and are those defined?

    Sometimes a program can invoke UB by neglecting to take some required steps
    that a situation calls for. That kind of situation has to be spelled out.

    For instance, if an input operation is done on an update stream after an output
    operation, without an intervening fflush, the behavior is undefined. If this
    was not spelled out, the behavior would have to be considered defined (since an
    update stream is one on which you can do a mixture of reads and writes, and if
    no combination is documented as broken, then you can assume it's not broken.)

    > This email and any file attachments(s) are confidential, may be legally
    > privileged, and are intended solely for use by the identified recipient


    The above footer is egregiously excessive and netiquette-violating. I
    understand it's not your fault. If you give me the e-mail address of whoever is
    responsible I will share with them an outsider's perspective. You should not
    have your posting stigmatized by reams of legalese garbage.
    Kaz Kylheku, Nov 6, 2011
    #6
  7. Quentin Pope

    Kaz Kylheku Guest

    On 2011-11-05, Keith Thompson <> wrote:
    > Note that the standard *could* have stated that the behavior of
    > invoking va_end() before consuming all the actual arguments with
    > va_arg() is undefined. There's even an argument to be made that
    > since it doesn't explicitly define the behavior, the behavior
    > is therefore undefined.


    That's essentially the same argument as saying that since the standard omits to
    say that you must write the line "foo" at the end of every text stream, it is
    UB by omission if a program fails to do that.

    Whenever something that the program /does not do/ causes its behavior
    to be undefined, that must be spelled out.
    Kaz Kylheku, Nov 6, 2011
    #7
  8. On Sat, 5 Nov 2011 22:32:21 +0000 (UTC), Quentin Pope
    <> wrote:

    >Consider the variadic function with the following prototype:
    >int foo(int num,...);
    >Here 'num' specifies the number of arguments, and assume that all the
    >arguments that should be passed to this function are of type int.
    >(My question has nothing to do with the definition of the function
    >foo, so don't bother about it.)
    >
    >If I call the function as:
    >foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >
    >Here I call foo with too many arguments than expected by its
    >definition.


    Not by its definition which specifies only that the function call must
    have at least one argument. So your concern is actually about foo's
    execution.

    For sake of discussion, I will assume you meant that num contains the
    number of variable arguments, not the total number of arguments.
    Consider the similar situation where argument 1 is actually 6.
    Everything "matches". But during execution, after processing argument
    3, foo decides there is some condition which warrants terminating its
    execution and returning to the caller. (Like free() determining that
    the input argument is NULL so there is no reason to do any more work.)
    However, foo is designed with sound programming practices in mind and
    as part of exiting cleans up after itself, including calling va_end
    (as required by 7.15.1.3-2). Since this situation occurs in lots of
    functions, C's utility as a programming language would be pretty
    limited if this by itself led to undefined behavior (and you would
    also see a requirement in the standard of the form "Do not call va_end
    after va_start unless va_arg has been called to process every argument
    in the calling sequence"). From the implementation's point of view
    (compiler, linker, run-time library, etc), this situation is the same
    as the one you described. The function will ignore some arguments. No
    one cares if it is because the value of num "hid" some of them or
    because the function decided they weren't needed. The result must be
    the same for C to be a practical language.

    --
    Remove del for email
    Barry Schwarz, Nov 6, 2011
    #8
  9. Barry Schwarz <> writes:
    > On Sat, 5 Nov 2011 22:32:21 +0000 (UTC), Quentin Pope
    > <> wrote:
    >
    >>Consider the variadic function with the following prototype:
    >>int foo(int num,...);
    >>Here 'num' specifies the number of arguments, and assume that all the
    >>arguments that should be passed to this function are of type int.
    >>(My question has nothing to do with the definition of the function
    >>foo, so don't bother about it.)
    >>
    >>If I call the function as:
    >>foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >>
    >>Here I call foo with too many arguments than expected by its
    >>definition.

    >
    > Not by its definition which specifies only that the function call must
    > have at least one argument. So your concern is actually about foo's
    > execution.
    >
    > For sake of discussion, I will assume you meant that num contains the
    > number of variable arguments, not the total number of arguments.
    > Consider the similar situation where argument 1 is actually 6.
    > Everything "matches". But during execution, after processing argument
    > 3, foo decides there is some condition which warrants terminating its
    > execution and returning to the caller. (Like free() determining that
    > the input argument is NULL so there is no reason to do any more work.)
    > However, foo is designed with sound programming practices in mind and
    > as part of exiting cleans up after itself, including calling va_end
    > (as required by 7.15.1.3-2). Since this situation occurs in lots of
    > functions, C's utility as a programming language would be pretty
    > limited if this by itself led to undefined behavior (and you would
    > also see a requirement in the standard of the form "Do not call va_end
    > after va_start unless va_arg has been called to process every argument
    > in the calling sequence"). From the implementation's point of view
    > (compiler, linker, run-time library, etc), this situation is the same
    > as the one you described. The function will ignore some arguments. No
    > one cares if it is because the value of num "hid" some of them or
    > because the function decided they weren't needed. The result must be
    > the same for C to be a practical language.


    I don't quite follow this.

    To be clear, I think we've established that there's no problem
    with a variadic function processing only some of its arguments
    (i.e., invoking va_end() without having used va_arg() to process
    all the arguments).

    But if the language were defined differently, I don't see that
    it would be much of a problem. I can't think of many realistic
    scenarios where it would even make sense to pass more arguments
    to a variadic function than it's going to process. For example,
    "foo(2,3,4,5,6,7,8);", though its behavior is well defined, is
    probably a programming error.

    The variadic functions I've seen either use a format string to
    specify the number and types of the following arguments, or use
    a special value, typically a null pointer, as an end marker.
    In both cases, a correct call will result in all the arguments
    being processed.

    If the standard did have the requirement you mention: "Do not call
    va_end after va_start unless va_arg has been called to process
    every argument in the calling sequence", what great inconvenience
    would it cause?

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Nov 6, 2011
    #9
  10. Quentin Pope

    Quentin Pope Guest

    On Sat, 05 Nov 2011 15:50:59 -0700, Keith Thompson wrote:

    > Quentin Pope <> writes:
    >> Consider the variadic function with the following prototype: int
    >> foo(int num,...);
    >> Here 'num' specifies the number of arguments, and assume that all the
    >> arguments that should be passed to this function are of type int. (My
    >> question has nothing to do with the definition of the function foo, so
    >> don't bother about it.)
    >>
    >> If I call the function as:
    >> foo(2,3,4,5,6,7,8);/*More arguments than expected*/
    >>
    >> Here I call foo with too many arguments than expected by its
    >> definition.
    >>
    >> Assuming that the prototype is visible in the scope of the function
    >> call and the definition of function foo itself does NOT produce any UB,
    >> please tell me if this function call produces UB or not.
    >>
    >> I am not able to find any explicit clause from C99 which deals with
    >> this behavior.
    >>
    >> Though I find that:
    >>
    >> 7.19.6.1 The fprintf function
    >> The fprintf function writes output to the stream pointed to by stream,
    >> under control of the string pointed to by format that specifies how
    >> subsequent arguments are converted for output. If there are
    >> insufficient arguments for the format, the behavior is undefined. If
    >> the format is exhausted while arguments remain, the excess arguments
    >> are evaluated (as always) but are otherwise ignored.
    >>
    >> This is highly specific to fprintf. I wonder if it applies to all
    >> variadic functions.

    >
    > I believe the behavior is well defined.
    >
    > The call itself cannot reasonably have undefined behavior, since foo()
    > itself *could* have called va_arg enough times to consume all the
    > arguments.
    >
    > Inside foo, the only potentially problematic think you're doing is
    > invoking va_end() before consuming all the arguments. Invoking va_arg()
    > when there are no more arguments is UB (C99 7.15.11.1p), but that
    > doesn't apply here.
    >
    > There's nothing in the description of va_end() that suggests that its
    > behavior depends on whether previous calls to va_arg() have consumed all
    > the arguments.
    >
    > (The behavior of fprintf() is described explicitly because there's no
    > requirement for fprintf() to use the <stdarg.h> mechanism; legacy
    > implementations would have used something else, perhaps <varargs.h>, and
    > fprintf() could be implemented in non-portable C or in a language other
    > than C.)
    >
    > [...]
    >>
    >> This email and any file attachments(s) are confidential, may be legally
    >> privileged, and are intended solely for use by the identified recipient

    > [29 lines deleted]
    >> responsibility for any loss or damage arising from the use of this
    >> email or attachments.

    >
    > If you can find a way to post without including this nonsense, I'm sure
    > we'd all appreciated it.


    Thanks for the helpful answers. Sorry about the legalese - I accidentally
    used the wrong sig block, which I have to include on work emails to avoid
    certain kinds of legal liability.

    // QP
    Quentin Pope, Nov 7, 2011
    #10
  11. Quentin Pope

    Tim Rentsch Guest

    Keith Thompson <> writes:

    > [snip]
    >
    > If the standard did have the requirement you mention: "Do not call
    > va_end after va_start unless va_arg has been called to process
    > every argument in the calling sequence", what great inconvenience
    > would it cause?


    The problem is it requires us to know things that we may
    not know, namely, the number and types of arguments remaining.
    Even if a motivating example isn't immediately obvious, that's
    a chilling prospect.
    Tim Rentsch, Jan 25, 2012
    #11
  12. Tim Rentsch <> writes:
    > Keith Thompson <> writes:
    >
    >> [snip]
    >>
    >> If the standard did have the requirement you mention: "Do not call
    >> va_end after va_start unless va_arg has been called to process
    >> every argument in the calling sequence", what great inconvenience
    >> would it cause?

    >
    > The problem is it requires us to know things that we may
    > not know, namely, the number and types of arguments remaining.
    > Even if a motivating example isn't immediately obvious, that's
    > a chilling prospect.


    The quoted discussion was a while ago, but I think what I had in mind is
    that calling va_end "too soon" would have undefined behavior. For
    example (assuming printf uses the <stdarg.h> mechanism), this:

    printf("%s\n", "arg1", "ignored");

    has well defined behavior; with the hypothetical rule, its behavior
    would be undefined. If the language were changed in this way, no
    implementations would have to change; the existing behavior would still
    be conforming.

    I do not advocate such a change; I was merely speculating on the
    consequences if the language had been defined that way from the
    beginning (or at least from 1989). As I wrote before, passing excess
    arguments to a variadic function is almost certainly a programming
    error; making such a call's behavior undefined would not be a great
    burden.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Jan 25, 2012
    #12
  13. Quentin Pope

    Tim Rentsch Guest

    Keith Thompson <> writes:

    > Tim Rentsch <> writes:
    >> Keith Thompson <> writes:
    >>
    >>> [snip]
    >>>
    >>> If the standard did have the requirement you mention: "Do not call
    >>> va_end after va_start unless va_arg has been called to process
    >>> every argument in the calling sequence", what great inconvenience
    >>> would it cause?

    >>
    >> The problem is it requires us to know things that we may
    >> not know, namely, the number and types of arguments remaining.
    >> Even if a motivating example isn't immediately obvious, that's
    >> a chilling prospect.

    >
    > The quoted discussion was a while ago, but I think what I had in mind is
    > that calling va_end "too soon" would have undefined behavior. For
    > example (assuming printf uses the <stdarg.h> mechanism), this:
    >
    > printf("%s\n", "arg1", "ignored");
    >
    > has well defined behavior; with the hypothetical rule, its behavior
    > would be undefined.


    That's an issue right there. We are taking a statement that
    has defined behavior now and changing the language so it has
    undefined behavior. Don't you see a problem with that?

    > If the language were changed in this way, no
    > implementations would have to change; the existing behavior would still
    > be conforming.


    Of course it would, because when there is undefined behavior
    any behavior whatsoever is conforming. Implementations might
    not be affected, but the semantics of programs are greatly
    affected.


    > I do not advocate such a change; I was merely speculating on the
    > consequences if the language had been defined that way from the
    > beginning (or at least from 1989). As I wrote before, passing excess
    > arguments to a variadic function is almost certainly a programming
    > error; making such a call's behavior undefined would not be a great
    > burden.


    Under the current rule, it is possible (to give one example) to
    write 'printf()' in a way that is portable across different
    implementations, doing all output with 'putchar()', and that works
    for calls like the one above. Under the revised rule, it isn't.

    The phrasing of your last sentence suggests that the notion of
    "excess arguments" is well-defined, and that when it is
    well-defined it's okay either to do the additional work necessary
    to collect them (when it can be determined how to do so) or
    accept that any such calls will result in undefined behavior
    (when it cannot be so determined). I think that's nuts. More
    specifically, I think the first part is wrong -- there are cases
    where the number of arguments that should be processed depends on
    other program state that is simply unknowable at the call site --
    and the second part is misguided -- functions that complete their
    task successfully should neither have to do more work to collect
    "extra" arguments nor be obliged to transgress into a domain of
    undefined behavior just because "extra" arguments were supplied.
    I see no reason to accept any additional programming burden
    along these lines, especially since allowing such cases to be
    undefined behavior produces no evident benefit.
    Tim Rentsch, Feb 1, 2012
    #13
  14. Tim Rentsch <> writes:
    > Keith Thompson <> writes:
    >> Tim Rentsch <> writes:
    >>> Keith Thompson <> writes:
    >>>> [snip]
    >>>>
    >>>> If the standard did have the requirement you mention: "Do not call
    >>>> va_end after va_start unless va_arg has been called to process
    >>>> every argument in the calling sequence", what great inconvenience
    >>>> would it cause?
    >>>
    >>> The problem is it requires us to know things that we may
    >>> not know, namely, the number and types of arguments remaining.
    >>> Even if a motivating example isn't immediately obvious, that's
    >>> a chilling prospect.

    >>
    >> The quoted discussion was a while ago, but I think what I had in mind is
    >> that calling va_end "too soon" would have undefined behavior. For
    >> example (assuming printf uses the <stdarg.h> mechanism), this:
    >>
    >> printf("%s\n", "arg1", "ignored");
    >>
    >> has well defined behavior; with the hypothetical rule, its behavior
    >> would be undefined.

    >
    > That's an issue right there. We are taking a statement that
    > has defined behavior now and changing the language so it has
    > undefined behavior. Don't you see a problem with that?


    I would certainly see a problem with that if that were what I suggested.
    I'm not talking about changing the language; I'm speculating about what
    problems, if any, would have resulted if the language had been defined
    that way from the beginning.

    When C was standardized in 1989, it *could have* been defined so that
    excess arguments cause undefined behavior. If it had, I don't believe
    it would have caused any great inconvenience. (Changing it now *would*
    cause great inconvenience.)

    If you thought such a hypothetical is not worth discussion, I would have
    no argument. But you obviously do think it's worth discussion.

    >> If the language were changed in this way, no
    >> implementations would have to change; the existing behavior would still
    >> be conforming.

    >
    > Of course it would, because when there is undefined behavior
    > any behavior whatsoever is conforming. Implementations might
    > not be affected, but the semantics of programs are greatly
    > affected.


    Yes. (Only the behavior of programs that pass excess arguments would be
    affected; in my hypothetical situation, such programs probably wouldn't
    have been written in the first place.)

    >> I do not advocate such a change; I was merely speculating on the
    >> consequences if the language had been defined that way from the
    >> beginning (or at least from 1989). As I wrote before, passing excess
    >> arguments to a variadic function is almost certainly a programming
    >> error; making such a call's behavior undefined would not be a great
    >> burden.

    >
    > Under the current rule, it is possible (to give one example) to
    > write 'printf()' in a way that is portable across different
    > implementations, doing all output with 'putchar()', and that works
    > for calls like the one above. Under the revised rule, it isn't.


    How so? An implementation that satisfies the current rule would also
    satisfy the hypothetical rule. In hypothetical-C, the call's behavior
    is undefined; it might as well do exactly what it does under the current
    rule.

    > The phrasing of your last sentence suggests that the notion of
    > "excess arguments" is well-defined,


    An "excess argument" to a variadic function is one for which va_arg() is
    not invoked before the function calls va_end().

    To put it another way, in hypothetical-C, the behavior of calling
    va_end() without having called va_arg() for all the actual arguments is
    undefined.

    > and that when it is
    > well-defined it's okay either to do the additional work necessary
    > to collect them (when it can be determined how to do so) or
    > accept that any such calls will result in undefined behavior
    > (when it cannot be so determined). I think that's nuts. More
    > specifically, I think the first part is wrong -- there are cases
    > where the number of arguments that should be processed depends on
    > other program state that is simply unknowable at the call site --


    Ok, that might be an interesting point.

    Do you suggest that there are *realistic* cases where a call to a
    variadic function passes N arguments, and the function itself processes
    fewer than N arguments, and where this does not indicate a logical error
    in the program?

    Now that you mention it, I can imagine such cases (I didn't think of any
    before). For example:

    const char *format_string;
    if (verbose) {
    format_string = "%d (%s)\n";
    }
    else {
    format_string = "%d\n";
    }
    printf(format_string, num, str);

    But *if* the hypothetical rule I discussed had been in the language from
    the beginning, programmers just wouldn't write code like the above. It
    wouldn't be that hard to write two printf calls.

    I believe this would have been a minor inconvenience, rarely
    encountered.

    > and the second part is misguided -- functions that complete their
    > task successfully should neither have to do more work to collect
    > "extra" arguments nor be obliged to transgress into a domain of
    > undefined behavior just because "extra" arguments were supplied.
    > I see no reason to accept any additional programming burden
    > along these lines, especially since allowing such cases to be
    > undefined behavior produces no evident benefit.


    I can imagine an implementation where a variadic function that
    fails to consume all its arguments might cause Bad Things To Happen.
    For example, each invocation of va_arg() might pop the corresponding
    argument off the stack; failing to do so might leave the stack in an
    inconsistent state. It could have much the same effect as passing
    two arguments to a non-variadic function with one parameter (say,
    by casting a function pointer to an inappropriate type), which has
    undefined behavior in C as it's currently defined.

    That's probably the scenario I had in mind when I suggested the
    hypothetical rule several months ago.

    Of course since C does define the behavior in such cases, no C
    conforming implementation can behave this way.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Feb 1, 2012
    #14
  15. Quentin Pope

    James Kuyper Guest

    On 02/01/2012 12:50 AM, Keith Thompson wrote:
    ....
    > Do you suggest that there are *realistic* cases where a call to a
    > variadic function passes N arguments, and the function itself processes
    > fewer than N arguments, and where this does not indicate a logical error
    > in the program?


    Yes: each of the arguments indicates a place to search for something;
    the function processes the arguments in turn until it's found what its
    searching for, ignoring any remaining arguments.
    --
    James Kuyper
    James Kuyper, Feb 1, 2012
    #15
  16. Quentin Pope

    Eric Sosman Guest

    On 2/1/2012 12:50 AM, Keith Thompson wrote:
    >[...]
    > Do you suggest that there are *realistic* cases where a call to a
    > variadic function passes N arguments, and the function itself processes
    > fewer than N arguments, and where this does not indicate a logical error
    > in the program?


    void logmessage(const char *format, ...) {
    if (logstream != NULL) {
    va_list ap;
    va_start(ap, format);
    vfprintf(logstream, format, ap);
    va_end(ap);
    } else {
    fputs("Logical error! (?)\n", stderr);
    }
    }

    --
    Eric Sosman
    d
    Eric Sosman, Feb 1, 2012
    #16
  17. Quentin Pope

    Willem Guest

    Keith Thompson wrote:
    ) Do you suggest that there are *realistic* cases where a call to a
    ) variadic function passes N arguments, and the function itself processes
    ) fewer than N arguments, and where this does not indicate a logical error
    ) in the program?

    In SQL, there is a function calles 'coalesce' which returns its first
    non-NULL argument. It doesn't need to process the remaining arguments.

    ) I can imagine an implementation where a variadic function that
    ) fails to consume all its arguments might cause Bad Things To Happen.
    ) For example, each invocation of va_arg() might pop the corresponding
    ) argument off the stack; failing to do so might leave the stack in an
    ) inconsistent state. It could have much the same effect as passing
    ) two arguments to a non-variadic function with one parameter (say,
    ) by casting a function pointer to an inappropriate type), which has
    ) undefined behavior in C as it's currently defined.

    Are you sure about that?
    I thought that was allowed and the second argument was simply ignored.


    SaSW, Willem
    --
    Disclaimer: I am in no way responsible for any of the statements
    made in the above text. For all I know I might be
    drugged or something..
    No I'm not paranoid. You all think I'm paranoid, don't you !
    #EOT
    Willem, Feb 1, 2012
    #17
  18. Quentin Pope

    Shao Miller Guest

    On 2/1/2012 00:50, Keith Thompson wrote:
    >
    > Ok, that might be an interesting point.
    >
    > Do you suggest that there are *realistic* cases where a call to a
    > variadic function passes N arguments, and the function itself processes
    > fewer than N arguments, and where this does not indicate a logical error
    > in the program?
    >


    I don't know if the following might qualify (please forgive any minor
    errors):

    int first_nonzero_value(int count, ...) {
    va_list varargs;
    int test;

    va_start(varargs, count);
    while (count--) {
    test = va_arg(varargs, int);
    if (test)
    break;
    continue;
    }
    va_end(varargs);
    return test;
    }

    --
    "The stationery store has moved. Aaargh!"
    Shao Miller, Feb 1, 2012
    #18
  19. Quentin Pope

    Ben Pfaff Guest

    Shao Miller <> writes:

    > On 2/1/2012 00:50, Keith Thompson wrote:
    >>
    >> Ok, that might be an interesting point.
    >>
    >> Do you suggest that there are *realistic* cases where a call to a
    >> variadic function passes N arguments, and the function itself processes
    >> fewer than N arguments, and where this does not indicate a logical error
    >> in the program?
    >>

    >
    > I don't know if the following might qualify (please forgive any minor
    > errors):
    >
    > int first_nonzero_value(int count, ...) {
    > va_list varargs;
    > int test;
    >
    > va_start(varargs, count);
    > while (count--) {
    > test = va_arg(varargs, int);
    > if (test)
    > break;
    > continue;
    > }
    > va_end(varargs);
    > return test;
    > }


    How about this:

    int max(int count, ...) {
    int max = INT_MIN;
    va_list args;

    va_start(args, count);
    do {
    int value = va_arg(args, int);
    if (value > max)
    max = value;
    } while (--count && max != INT_MAX);
    va_end(args);

    return max;
    }
    --
    "Your correction is 100% correct and 0% helpful. Well done!"
    --Richard Heathfield
    Ben Pfaff, Feb 1, 2012
    #19
  20. Willem <> writes:
    > Keith Thompson wrote:
    > ) Do you suggest that there are *realistic* cases where a call to a
    > ) variadic function passes N arguments, and the function itself processes
    > ) fewer than N arguments, and where this does not indicate a logical error
    > ) in the program?
    >
    > In SQL, there is a function calles 'coalesce' which returns its first
    > non-NULL argument. It doesn't need to process the remaining arguments.
    >
    > ) I can imagine an implementation where a variadic function that
    > ) fails to consume all its arguments might cause Bad Things To Happen.
    > ) For example, each invocation of va_arg() might pop the corresponding
    > ) argument off the stack; failing to do so might leave the stack in an
    > ) inconsistent state. It could have much the same effect as passing
    > ) two arguments to a non-variadic function with one parameter (say,
    > ) by casting a function pointer to an inappropriate type), which has
    > ) undefined behavior in C as it's currently defined.
    >
    > Are you sure about that?
    > I thought that was allowed and the second argument was simply ignored.


    You're right. The context of the quoted paragraph was a hypothetical
    version of C that doesn't permit invoking va_end() before invoking
    va_arg() for all actual arguments. An implementation as I described
    could implement such a hypothetical C, and implementing it *might* be
    more convenient than implementing something that handles C as it's
    actually defined. It would not be a conforming implementation of C as
    it's actually defined.

    This all started with an idle speculation of mine several months ago
    about whether, if C had *originally* required all variadic arguments to
    be processed, it would create any great inconvenience. At the time, I
    didn't have any examples where it would be a problem. Several such
    examples have now been offered.

    The C89 committee *could* have imposed such a requirements, and if they
    had, programmers would have managed, but there are some valid techniques
    that couldn't be used.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Feb 1, 2012
    #20
    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. Colin Walters
    Replies:
    2
    Views:
    508
    Ben Pfaff
    Feb 13, 2004
  2. Ross A. Finlayson
    Replies:
    19
    Views:
    580
    Keith Thompson
    Mar 10, 2005
  3. Replies:
    2
    Views:
    332
    Dave Thompson
    Feb 27, 2006
  4. dd
    Replies:
    2
    Views:
    366
    tmp123
    Mar 28, 2006
  5. Replies:
    5
    Views:
    351
Loading...

Share This Page