By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,738 Members | 1,730 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,738 IT Pros & Developers. It's quick & easy.

variadic without va_arg

P: n/a

I don't particularly enjoy using the va_start macro family, and I've
noticed that the following code works. I'm a little concerned about
the fact that the prototypes for foo do not match. Is this safe?

[tmp]$ cat q.c
extern int foo(int x,...);

int
main (int argc, char const*const* argv) /*:)*/
{
foo(0);
foo(1,8);
foo(2,3,7);
foo(4,1,2,3,4);
return 0;
}
[tmp]$ cat r.c

int
foo(int num, int a, int b, int c)
{
switch(num) {
case 0: return 0;
case 1: return a;
case 2: return a+b;
case 3: return a+b+c;
default: return -1;
}
}

Apr 29 '06 #1
Share this Question
Share on Google+
5 Replies


P: n/a
Bill Pursell wrote:
I don't particularly enjoy using the va_start macro family, and I've
noticed that the following code works. I'm a little concerned about
the fact that the prototypes for foo do not match. Is this safe?

[tmp]$ cat q.c
extern int foo(int x,...);

int
main (int argc, char const*const* argv) /*:)*/
{
foo(0);
foo(1,8);
foo(2,3,7);
foo(4,1,2,3,4);
return 0;
}
[tmp]$ cat r.c

int
foo(int num, int a, int b, int c)
{
switch(num) {
case 0: return 0;
case 1: return a;
case 2: return a+b;
case 3: return a+b+c;
default: return -1;
}
}


According to the C99 standard (section 6.9.1) "If a function that
accepts a variable number of arguments is defined without a parameter
type list that ends with the ellipsis notation, the behavior is undefined."

Your code works, because your compiler and processor architecture use an
argument passing convention compatible with the 1970s style C. At that
time functions like printf were indeed called with fewer or more
arguments than their specification and extra arguments or clever pointer
arithmetic were used to access the remaining arguments. These tricks
were however not portable, and prompted the development of the Unix
vararg and later the ANSI stdarg facilities.

Having said that I admit that processor architects and compiler vendors
go to extreme lengths to make legacy code work. I tried your example on
some architectures I thought it would bomb (a SPARC, an Itanium, and an
Alpha), and it worked correctly. Still, there's no reason to write
non-portable code: at the very least you demonstrate you're not playing
by the rules. If I was reading your code I would worry that other
problems might also be lurking in it.

--
Diomidis Spinellis
Code Quality: The Open Source Perspective (Addison-Wesley 2006)
http://www.spinellis.gr/codequality?clc
Apr 29 '06 #2

P: n/a
Bill Pursell schrieb:
I don't particularly enjoy using the va_start macro family, and I've
noticed that the following code works. I'm a little concerned about
the fact that the prototypes for foo do not match. Is this safe?

[tmp]$ cat q.c
extern int foo(int x,...);

int
main (int argc, char const*const* argv) /*:)*/
{
foo(0);
foo(1,8);
foo(2,3,7);
foo(4,1,2,3,4);
return 0;
}
[tmp]$ cat r.c

int
foo(int num, int a, int b, int c)
{
switch(num) {
case 0: return 0;
case 1: return a;
case 2: return a+b;
case 3: return a+b+c;
default: return -1;
}
}


No, this comes into conflict with C99, 6.7.5.3#9 and #15;
you are using incompatible function types.
You are invoking UB.

Let us go for more practical reasons:
- If you do not use int, then you might run into unpleasant
surprises.
int foo(int num, long a, short b, char c)
with LONG_MAX > INT_MAX and sizeof long > sizeof int
int foo(int num, long double a, double b, float c)
both might give you trouble.
- In addition, passing hundred arguments to foo() may be
harmful in quite unexpected ways.
- Another thing: You might have different calling conventions,
maybe only for a certain number of parameters. Merriment ensues
for fixed parameter order.
- Your lint tool or linker warns you about it.

If you really dislike variable argument list handling that much,
then do not use variable argument lists -- you nearly always can
roll an alternative avoiding them at some cost.
Cheers
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Apr 29 '06 #3

P: n/a

Diomidis Spinellis wrote:
Bill Pursell wrote:
I don't particularly enjoy using the va_start macro family, and I've
noticed that the following code works. I'm a little concerned about
the fact that the prototypes for foo do not match. Is this safe?

[tmp]$ cat q.c
extern int foo(int x,...);

int
main (int argc, char const*const* argv) /*:)*/
{
foo(0);
foo(1,8);
foo(2,3,7);
foo(4,1,2,3,4);
return 0;
}
[tmp]$ cat r.c

int
foo(int num, int a, int b, int c)
{
switch(num) {
case 0: return 0;
case 1: return a;
case 2: return a+b;
case 3: return a+b+c;
default: return -1;
}
}


According to the C99 standard (section 6.9.1) "If a function that
accepts a variable number of arguments is defined without a parameter
type list that ends with the ellipsis notation, the behavior is undefined."

Your code works, because your compiler and processor architecture use an
argument passing convention compatible with the 1970s style C. At that
time functions like printf were indeed called with fewer or more
arguments than their specification and extra arguments or clever pointer
arithmetic were used to access the remaining arguments. These tricks
were however not portable, and prompted the development of the Unix
vararg and later the ANSI stdarg facilities.

Having said that I admit that processor architects and compiler vendors
go to extreme lengths to make legacy code work. I tried your example on
some architectures I thought it would bomb (a SPARC, an Itanium, and an
Alpha), and it worked correctly. Still, there's no reason to write
non-portable code: at the very least you demonstrate you're not playing
by the rules. If I was reading your code I would worry that other
problems might also be lurking in it.


Would it be portable to simply change the prototypes so that the caller
has the interface:
extern int foo(int num, ...)
while the definition of the function gets:
int foo(int num, int a, int b, int c, ...)?

That seems to satisfy the section of the standard you quote above, but
it still feels wrong.

Apr 29 '06 #4

P: n/a
Bill Pursell wrote:
Diomidis Spinellis wrote:
Bill Pursell wrote:
I don't particularly enjoy using the va_start macro family, and I've
noticed that the following code works. I'm a little concerned about
the fact that the prototypes for foo do not match. Is this safe?

[...]
Would it be portable to simply change the prototypes so that the caller
has the interface:
extern int foo(int num, ...)
while the definition of the function gets:
int foo(int num, int a, int b, int c, ...)?

That seems to satisfy the section of the standard you quote above, but
it still feels wrong.


Still wrong; see Michael Mair's reply to your original post.
Apr 29 '06 #5

P: n/a
On 29 Apr 2006 00:16:30 -0700, "Bill Pursell" <bi**********@gmail.com>
wrote in comp.lang.c:

I don't particularly enjoy using the va_start macro family, and I've
noticed that the following code works. I'm a little concerned about
the fact that the prototypes for foo do not match. Is this safe?

[tmp]$ cat q.c
extern int foo(int x,...);

int
main (int argc, char const*const* argv) /*:)*/
{
foo(0);
foo(1,8);
foo(2,3,7);
foo(4,1,2,3,4);
return 0;
}
[tmp]$ cat r.c

int
foo(int num, int a, int b, int c)
{
switch(num) {
case 0: return 0;
case 1: return a;
case 2: return a+b;
case 3: return a+b+c;
default: return -1;
}
}


This only "works" as you want because your compiler uses a method
required for pre-standard C for passing arguments. That may only be
true with the particular set of compiler options that you use. If you
change options, such as for optimization, it might well break.

There are implementations where this will not work under any
circumstances. They use completely different methods of passing
arguments to variadic and non-variadic functions. I have used several
such compilers over the years.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~a...FAQ-acllc.html
Apr 29 '06 #6

This discussion thread is closed

Replies have been disabled for this discussion.