Google 群组不再支持新的 Usenet 帖子或订阅项。历史内容仍可供查看。

va_arg()

已查看 8 次
跳至第一个未读帖子

LIT

未读,
2006年10月26日 10:26:242006/10/26
收件人
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

Richard Tobin

未读,
2006年10月26日 11:02:052006/10/26
收件人
In article <1161872784.7...@k70g2000cwa.googlegroups.com>,
LIT <tony.li...@dana.com> 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

mark_b...@pobox.com

未读,
2006年10月26日 11:03:092006/10/26
收件人

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" :-)

LIT

未读,
2006年10月26日 11:23:022006/10/26
收件人

mark_b...@pobox.com schreef:

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[i] = 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

David Resnick

未读,
2006年10月26日 11:31:272006/10/26
收件人

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

mark_b...@pobox.com

未读,
2006年10月26日 11:33:322006/10/26
收件人

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

Laurent Deniau

未读,
2006年10月26日 11:45:242006/10/26
收件人

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.

David Tiktin

未读,
2006年10月26日 12:42:332006/10/26
收件人
On 26 Oct 2006, "LIT" <tony.li...@dana.com> 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[i] = 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

Eric Sosman

未读,
2006年10月26日 13:21:462006/10/26
收件人

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[i] = 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....@sun.com

Christopher Benson-Manica

未读,
2006年10月26日 13:53:192006/10/26
收件人
Eric Sosman <Eric....@sun.com> 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.

David Resnick

未读,
2006年10月26日 14:39:202006/10/26
收件人
Christopher Benson-Manica wrote:
> Eric Sosman <Eric....@sun.com> 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

Keith Thompson

未读,
2006年10月26日 14:55:052006/10/26
收件人
Christopher Benson-Manica <at...@norge.freeshell.org> writes:
> Eric Sosman <Eric....@sun.com> 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) ks...@mib.org <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

未读,
2006年10月26日 15:15:592006/10/26
收件人

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.

Eric Sosman

未读,
2006年10月26日 15:29:482006/10/26
收件人

Keith Thompson wrote On 10/26/06 14:55,:


> Christopher Benson-Manica <at...@norge.freeshell.org> writes:
>
>>Eric Sosman <Eric....@sun.com> 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....@sun.com

David Resnick

未读,
2006年10月26日 15:57:272006/10/26
收件人

Keith Thompson wrote:
> "David Resnick" <lndre...@gmail.com> writes:
> > Christopher Benson-Manica wrote:
> >> Eric Sosman <Eric....@sun.com> 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

Keith Thompson

未读,
2006年10月26日 16:55:132006/10/26
收件人
"David Resnick" <lndre...@gmail.com> 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.

Christopher Benson-Manica

未读,
2006年10月27日 06:44:492006/10/27
收件人
Keith Thompson <ks...@mib.org> 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.

0 个新帖子