variadic function


Q

Quentin Pope

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.
 
Ad

Advertisements

K

Keith Thompson

Quentin Pope said:
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.
 
J

James Kuyper

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).
 
K

Keith Thompson

James Kuyper said:
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.

[...]
 
K

Kaz Kylheku

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.
 
K

Kaz Kylheku

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.
 
Ad

Advertisements

K

Kaz Kylheku

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.
 
B

Barry Schwarz

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.
 
K

Keith Thompson

Barry Schwarz said:
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?
 
Q

Quentin Pope

Quentin Pope said:
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
 
T

Tim Rentsch

Keith Thompson said:
[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.
 
Ad

Advertisements

K

Keith Thompson

Tim Rentsch said:
Keith Thompson said:
[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.
 
T

Tim Rentsch

Keith Thompson said:
Tim Rentsch said:
Keith Thompson said:
[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.
 
K

Keith Thompson

Tim Rentsch said:
Keith Thompson said:
Tim Rentsch said:
[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.
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.)
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.
 
J

James Kuyper

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.
 
E

Eric Sosman

[...]
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);
}
}
 
Ad

Advertisements

W

Willem

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
 
S

Shao Miller

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;
}
 
B

Ben Pfaff

Shao Miller said:
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;
}
 
Ad

Advertisements

K

Keith Thompson

Willem said:
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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top