473,322 Members | 1,431 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,322 software developers and data experts.

varargs's doubt

Hi,
I have a function which takes a variable number of arguments.It then
calls va_start macro to initialize the argument list.But here in my case
I have a special builtin va_start provided by the compiler which I use
for performance gains.Then the variable arguments are passed to another
varargs function which then uses a builtin va_arg macro to get the
arguments.
Iam getting an error from the compiler in the called function when a
local va_list variable is assigned to the passed va_list.Is this not
defined by the standard ?

I get the following error code :

gs.c(749): warning #563: initialization with "{...}" expected for
aggregate object
va_list ap = arglist;
^

gs.c(749): error: a value of type "struct __builtin_va_list_struct_type
*" cannot be used to initialize an entity of type "struct
__builtin_va_list_struct_type"
va_list ap = arglist;
^

compilation aborted for gs.c (code 2)
To show you how the code is in the source.Apologize to not being able to
produce a minimal compilable program exhibiting the problem.

gsscanf(char * fmt,...){
va_list ap;
Nov 14 '05 #1
9 4946
I could solve the above compilation error by changing the "va_list
arglist " to "va_list ap" in the gsScanfV() function parameters and
removing the assignmet line (va_list ap = arglist;).


I also got another way to solve the problem.Changed the called function
as below :
gsScanfV(char *fmt,va_list arglist)
{
va_list ap ;
va_copy(arglist,ap);
Nov 14 '05 #2
On Tue, 05 Apr 2005 11:45:57 +0530, grid
<pr******@gmail.com> wrote:
I have a function which takes a variable number of arguments.It then
calls va_start macro to initialize the argument list.But here in my case
I have a special builtin va_start provided by the compiler which I use
for performance gains.Then the variable arguments are passed to another
varargs function which then uses a builtin va_arg macro to get the
arguments.
For a start, I am very dubious that there are any significant
performance gains in doing that. If there were, the library
implementors should have defined the macrose in terms of the builtins in
the first place. Have you actually profiled the code and found a
significant difference there?

Try it with the standard ones, does that work? Are you including a
special header to get that builtin functionality?
Iam getting an error from the compiler in the called function when a
local va_list variable is assigned to the passed va_list.Is this not
defined by the standard ?
It doesn't seem to be. C99 says that you can pass it as an argument to
a function, and to va_start/va_arg/va_end/va_copy, but the existence of
va_copy() would seem to imply that just assigning it is not a defined
behaviour (gcc/glibc seems to accept it, but that doesn't imply that
anything else would).
I get the following error code :

gs.c(749): warning #563: initialization with "{...}" expected for
aggregate object
va_list ap = arglist;
^

gs.c(749): error: a value of type "struct __builtin_va_list_struct_type
*" cannot be used to initialize an entity of type "struct
__builtin_va_list_struct_type"
va_list ap = arglist;
^
It looks as though the compiler explicitly excludes variables of type
__builtin_va_list_struct_type from being assigned. It may be
implemented as an array, for instance.

Something you could do is look at the preprocessed output and search for
__builtin_va_list_struct_type, see if that is defined anywhere.
I could solve the above compilation error by changing the "va_list
arglist " to "va_list ap" in the gsScanfV() function parameters and
removing the assignmet line (va_list ap = arglist;).


So why don't you do that? You said above that you are concerned about
speed, why do an extra assignment?

Chris C
Nov 14 '05 #3
> So why don't you do that? You said above that you are concerned about
speed, why do an extra assignment?


Chris,its not a piece of code written by me and hence wanted to confirm
if I am not treading on undefined land because of my changes.Iam not
sure why the original author chose to have an extra assignment.

Which one of the two solutions look graceful ?

TIA,
Nov 14 '05 #4
On Tue, 05 Apr 2005 20:03:11 +0530, grid
<pr******@gmail.com> wrote:
So why don't you do that? You said above that you are concerned about
speed, why do an extra assignment?


Chris,its not a piece of code written by me and hence wanted to confirm
if I am not treading on undefined land because of my changes.Iam not
sure why the original author chose to have an extra assignment.

Which one of the two solutions look graceful ?


I would use the parameter directly, that's the way with least code (and
therefore the least to go wrong). Having an extra variable prompts the
question "why? was there some reason it was done that way?".

If va_copy() exists in your implementation (it's a C99 feature not in
C89) then by all means use that.

(Although I'd love to know how they managed to implement it such that a
va_list can be passed as a parameter but not assigned directly, that
smacks of special coding...)

Chris C
Nov 14 '05 #5
>On Tue, 05 Apr 2005 11:45:57 +0530, grid
<pr******@gmail.com> wrote:
I have a function which takes a variable number of arguments.It then
calls va_start macro to initialize the argument list.But here in my case
I have a special builtin va_start provided by the compiler which I use
for performance gains.
It is not a "performance" item, but rather a correctness issue.
Using the __builtin_* functions will do the right thing; trying
to fake it out by hand will not, in some cases.

In any case, the __builtin_* stuff is hidden behind macros, and
you, as the programmer, should just use the advertised interface
(va_start, va_arg, va_end, and in this case, nothing else).
Then the variable arguments are passed to another varargs
function which then uses a builtin va_arg macro to get the arguments.
This is allowed, but *only* via this form:

SOMETYPE like_printf(const char *fmt, va_list ap) {
/* code that uses va_arg(ap, TYPE) */
}

(of course SOMETYPE can be any valid function-return type, the name
of the function can be any valid function name, the "fmt" argument
can be something else, and so on). The important point here is
that "ap" is a formal parameter of type "va_list". If va_list is
a typedef for an array type, C's peculiar treatment of arrays will
automatically adjust it to be "pointer to first element of that
array" instead (as is happening here).

In article <sl******************@ccserver.keris.net>
Chris Croughton <ch***@keristor.net> wrote:
For a start, I am very dubious that there are any significant
performance gains in doing that. If there were, the library
implementors should have defined the macrose in terms of the builtins in
the first place.
You are right, and they did. (I am gazing into my crystal ball and
I see that "grid" is using a PowerPC or similar architecture.)
It doesn't seem to be. C99 says that you can pass it as an argument to
a function, and to va_start/va_arg/va_end/va_copy, but the existence of
va_copy() would seem to imply that just assigning it is not a defined
behaviour
This is correct. In particular, on the machine he has, the contents
of <stdarg.h> include something like this:

typedef struct __builtin_va_list_struct_type va_list[1];

struct __builtin_va_list_struct_type {
int __ni; /* number of saved int regs */
int __i[__VA_MAXI]; /* and their values */
int __nf; /* number of saved FPU regs */
float __f[__VA_MAXF]; /* and their values */
int *__rest; /* other values (on stack) */
};
(gcc/glibc seems to accept it, but that doesn't imply that
anything else would).


The machine on which va_copy is a simple copy, by contrast, has this
in its <stdarg.h>:

typedef char *va_list;

Note the (second) error message here:
gs.c(749): warning #563: initialization with "{...}" expected for
aggregate object
va_list ap = arglist;
gs.c(749): error: a value of type "struct __builtin_va_list_struct_type
*" cannot be used to initialize an entity of type "struct
__builtin_va_list_struct_type"
va_list ap = arglist;
^
Clearly "arglist" has type "pointer to struct ...", while "ap" needs
an initializer of type "struct ...", enclosed in braces. Clearly
"ap" is actually an array type, while "arglist" is a pointer type.
How can "arglist" be a pointer type when it also uses "va_list"
as its declaration? The answer lies in The Rule about arrays and
pointers in C.

Let me show the two lines in question one more time here:

int gsScanfV(const char *fmt, va_list arglist) {
va_list ap = arglist;
...
}

If va_list is a typedef for an array type (and it is), we now
have the same thing we would get with:

void f(char arg_s[1]) {
char local_s[1] = arg_s;
...
}

Here arg_s has type "char *" (pointer to char), while local_s has
type "char [1]" (array 1 of char). The initializer is invalid
and a diagnostic must occur.
I could solve the above compilation error by changing the "va_list
arglist " to "va_list ap" in the gsScanfV() function parameters and
removing the assignmet line (va_list ap = arglist;).


This is a good solution *provided* it is OK to destroy the va_list
object whose element-address has been passed (courtesy, again, of
The Rule). Consider for instance:

int message(const char *fmt, ...) {
va_list ap;
int ret;

va_start(ap, fmt);
ret = vfprintf(stdout, fmt, ap);
va_end(ap);
return ret;
}

This function works just like printf(). The vfprintf() function,
which receives the address of the first (and only) element of the
"struct" containing the various registers, is allowed to modify
ap[0].__ni (AKA ap->__ni) and ap->__nf and ap->__rest. Not only
is this allowed, it actually happens. Now suppose we decide that
message() should not only print to stdout, but also to a message-file.
So we try change message() to read:

int message(const char *fmt, ...) {
va_list ap;
int ret;
extern FILE *logfile;

va_start(ap, fmt);
ret = vfprintf(stdout, fmt, ap);
ret = vfprintf(logfile, fmt, ap); /* OOPS! WRONG! BUG! */
va_end(ap);
return ret;
}

As before, vfprintf() -- the first call, to stdout -- modifies
ap->__ni and ap->__nf and ap->__rest. The second call receives
these modified values -- and prints junk.

On another machine, where "va_list" is just a typedef for "char *",
the first vfprintf() modifies a *copy* of (the entire value of)
ap, instead of the (single) array element ap[0]. The second
vfprintf() gets another new, fresh copy, and it all works.

In other words, this bug-ridden modified message() function works
on some machines, and fails on others -- just what one can expect
of code with undefined behavior.

One fix, and the only one available in C89, is to rewrite
message() itself:

int message(const char *fmt, ...) {
va_list ap;
int ret;
extern FILE *logfile;

va_start(ap, fmt);
ret = vfprintf(stdout, fmt, ap);
va_end(ap);
va_start(ap, fmt);
ret = vfprintf(logfile, fmt, ap); /* OK this time */
va_end(ap);
return ret;
}

The first va_end cleans up, and the second va_start "resets" the
array contents, so that the second vfprintf() works.

This is all well and good until we go to write the missing vmessage()
function, a la fprintf() and vfprintf(). Our mesage() has the
actual "fmt, ..." parameters and can use va_start() twice -- but
vmessage() should read something like:

int vmessage(const char *fmt, va_list ap) {
extern FILE *logfile;

(void) vfprintf(stdout, fmt, ap);
return vfprintf(logfile, fmt, ap); /* OOPS! WRONG! BUG! */
}

Here we have the same problem as last time: we destroy the values
in ap[0].__ni, ap[0].__nf, and so on, so that the second vfprintf()
prints junk. But in C89, there is no way to fix it. We can
instead require two copies of the va_list:

int ugly_vmessage(const char *fmt, va_list ap1, va_list ap2) {
extern FILE *logfile;

(void) vfprintf(stdout, fmt, ap1);
return vfprintf(logfile, fmt, ap2); /* OK now */
}

which requires the caller to do two va_start()s. This is the only
thing we *can* do. In C99, however, we have the new va_copy macro,
so we can fix it the other way:

int vmessage(const char *fmt, va_list ap) {
extern FILE *logfile;
va_list copy;

va_copy(copy, ap);
(void) vfprintf(stdout, fmt, copy);
va_end(copy); /* see note below */

return vfprintf(logfile, fmt, ap); /* OK now */
}

Now vmessage() still destroys its "va_list" argument on the way
out, but it first makes a copy and uses that to print to stdout.
Note, my C99 draft standard implies that the above va_end() call
is required -- that a va_copy() has a sort of implied va_start()
-- although this is just from an example and not from the text
itself. I would check the final standard before assuming this is
correct.

Without seeing all the code for gsScanfV() and its caller(s),
I cannot say whether a va_copy() is required. (My crystal ball
is a bit cloudy.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #6
On 5 Apr 2005 17:25:21 GMT, Chris Torek
<no****@torek.net> wrote:
In article <sl******************@ccserver.keris.net>
Chris Croughton <ch***@keristor.net> wrote:
It doesn't seem to be. C99 says that you can pass it as an argument to
a function, and to va_start/va_arg/va_end/va_copy, but the existence of
va_copy() would seem to imply that just assigning it is not a defined
behaviour


This is correct. In particular, on the machine he has, the contents
of <stdarg.h> include something like this:

typedef struct __builtin_va_list_struct_type va_list[1];

struct __builtin_va_list_struct_type {
int __ni; /* number of saved int regs */
int __i[__VA_MAXI]; /* and their values */
int __nf; /* number of saved FPU regs */
float __f[__VA_MAXF]; /* and their values */
int *__rest; /* other values (on stack) */
};


Aha! Yes, that explains why assignment doesn't work. So would

typedef unsigned char va_list[128];

for that matter.
(gcc/glibc seems to accept it, but that doesn't imply that
anything else would).


The machine on which va_copy is a simple copy, by contrast, has this
in its <stdarg.h>:

typedef char *va_list;


Yes, that (or similar with void*) is the first implementation of varargs
I encountered, many years ago...
I could solve the above compilation error by changing the "va_list
arglist " to "va_list ap" in the gsScanfV() function parameters and
removing the assignmet line (va_list ap = arglist;).


This is a good solution *provided* it is OK to destroy the va_list
object whose element-address has been passed (courtesy, again, of
The Rule). Consider for instance:


[big snip describing why va_copy is needed]

Thanks! I've never seen such a comprehensive (and comprehensible)
description of the problem and solution before.
Without seeing all the code for gsScanfV() and its caller(s),
I cannot say whether a va_copy() is required. (My crystal ball
is a bit cloudy.)


It's a pity va_copy() wasn't in C89...

Thanks for the explanations...

Chris C
Nov 14 '05 #7
> This is correct. In particular, on the machine he has, the contents
of <stdarg.h> include something like this:

typedef struct __builtin_va_list_struct_type va_list[1];

struct __builtin_va_list_struct_type {
int __ni; /* number of saved int regs */
int __i[__VA_MAXI]; /* and their values */
int __nf; /* number of saved FPU regs */
float __f[__VA_MAXF]; /* and their values */
int *__rest; /* other values (on stack) */
};
(gcc/glibc seems to accept it, but that doesn't imply that
anything else would).


I tried to see the <stdarg.h> file for my implementation (its glibc on
x86_64 for Linux ),and I dont seem to find the builtin_va_list_struct_type.
So I wrote a simple program,and I am pasting the preprocessed output.The
<stdarg.h> for my implementation is similar to this
http://www.elrincondelprogramador.co...cs/54/stdarg.h .

Program :
--------
#include<stdio.h>
#include<stdarg.h>

void myprintf(const char *fmt,...)
{
va_list ap;
int a,b;

va_start(ap,fmt);
a = va_arg(ap,int);
b = va_arg(ap,int);
va_end(ap);
printf("a == %d \n b == %d \n",a ,b);
}

int main()
{
myprintf("%d %d\n",1,2);
return 0;
}

Preprocessed Output (Edited the irrelevent parts ):
---------------------------------------------------

# 1 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 1 3
# 43 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 3
typedef __builtin_va_list __gnuc_va_list;

# 2 "test.c" 2
# 1 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 1 3
# 110 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 3
typedef __gnuc_va_list va_list;
# 3 "test.c" 2

void myprintf(const char *fmt,...)
{
va_list ap;
int a,b;
__builtin_stdarg_start((ap),fmt);
a = __builtin_va_arg(ap,int);
b = __builtin_va_arg(ap,int);
__builtin_va_end(ap);
printf("a == %d \n b == %d \n",a ,b);

}
int main()
{
myprintf("%d %d\n",1,2);
return 0;
}

I am just invoking usin the default options, and hence should not invoke
with -C99 flags , moreover programs are here are not compiled with
-C99 flag as yet.So probably the implementation provides a va_copy
implementation for the C89 mode too.The compiler is gcc version 3.2.3
20030502 (Red Hat Linux 3.2.3-34) for the RHEL.

I would like to see the actual implementations of the __builtin_* types
, but could not find in the include dir.

Thanks,
Nov 14 '05 #8
On Wed, 06 Apr 2005 19:22:22 +0530, grid
<pr******@gmail.com> wrote:
I tried to see the <stdarg.h> file for my implementation (its glibc on
x86_64 for Linux ),and I dont seem to find the builtin_va_list_struct_type.
If it's builtin it probably means that it's a type the compiler knows
about without any headers (like it does int, double etc.). As it
happens I've just build GCC 3.4.3 so I have the source tree lying
around...
So I wrote a simple program,and I am pasting the preprocessed output.The
<stdarg.h> for my implementation is similar to this
http://www.elrincondelprogramador.co...cs/54/stdarg.h .

Preprocessed Output (Edited the irrelevent parts ):
---------------------------------------------------

# 1 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 1 3
# 43 "/usr/lib/gcc-lib/x86_64-redhat-linux/3.2.3/include/stdarg.h" 3
typedef __builtin_va_list __gnuc_va_list;
There it is. On this system it seems to be __builtin_va_list instead.
Looking at the GCC Source I find that it is indeed recognised as an
internal type (I'm not going to dig and work out what exactly).
I would like to see the actual implementations of the __builtin_* types
, but could not find in the include dir.


You won't find them in the headers, they are internal to the compiler.
You could download the compiler source (go for gcc-core, it's a lot
smaller than the whole thing) and try to work out what that is doing...

The point is that the internals of such macros are only of interest
academically. If you try to use such undocumented and implementation
specific things then you will run into trouble as soon as the
implementers change them, which they are quite likely to do. And being
macros you won't gain anything by expanding them by hand anyway...

Chris C
Nov 14 '05 #9
[I wrote, in part:]
This is correct. In particular, on the machine he has, the contents
of <stdarg.h> include something like this:

typedef struct __builtin_va_list_struct_type va_list[1];

struct __builtin_va_list_struct_type {
int __ni; /* number of saved int regs */
int __i[__VA_MAXI]; /* and their values */
int __nf; /* number of saved FPU regs */
float __f[__VA_MAXF]; /* and their values */
int *__rest; /* other values (on stack) */
};

In article <q1**************@news.oracle.com>
grid <pr******@gmail.com> wrote:I tried to see the <stdarg.h> file for my implementation (its glibc on
x86_64 for Linux), and I dont seem to find the builtin_va_list_struct_type.
Aha. I thought you probably had some other compiler, and were
most likely on a PowerPC-like machine. (Just goes to show that
my crystal ball is cloudier than I would like. :-) )
So I wrote a simple program, and I am pasting the preprocessed output. ...
You will not find the expansion of the structure here. The reason
is simple enough: the GNU GCC folks have done the sensible thing,
and put the important contents of <stdarg.h> -- which must match
the compiler's code for __builtin_stdarg_start, and is therefore
known to the compiler -- inside the compiler. (See how neat it
is? All the magic is inside the compiler: the definition of the
structure itself, the code that fills it in, and the code that uses
it, are all in the same place, and hence cannot get out of sync
with each other.)

Unfortunately (?), this has the side effect that the magic is now
invisible to the casual programmer. The only place to see it all
happen is in the source code to the compiler.
I would like to see the actual implementations of the __builtin_*
types, but could not find in the include dir.


Alas, they are off-topic here, and the on-topic groups (gnu.gcc.*)
are mostly dead on my news server (gnu.g++.bug gets a lot of spam
and an occasional actual bug report).
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Nov 14 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Ian Partridge | last post by:
Hi, I want to write a varargs function which then passes its parameters to another varargs function. I have RTFFAQ: http://www.eskimo.com/~scs/C-faq/q15.12.html which says that the other...
2
by: Rick Anderson | last post by:
I want to store a "va_list" in a structure (which gets passed around from function to function). I cannot use the va_copy() routine to create a copy of the varargs (it looks like it uses stack...
77
by: muttaa | last post by:
Hello all, My doubt is going to be so primitive that i ask you all to forgive me beforehand.... Here's the code snippet: int main() { int x=5;
11
by: Bob Nelson | last post by:
I don't remember seeing the term ``doubt'' used much in c.l.c. back in the 90's. When did this word become nearly synonymous with ``question'' or ``query'' and does it have static duration?
122
by: ivan | last post by:
hi all, if I have: if(A && B || C) which operation gets executed first? If I remeber well should be &&, am I correct? thanks
5
by: Paulo | last post by:
Hi, I have a RadioButtonList and I need to do some verifications on a "OnChange" event on client... because on classic asp/html I just add a "onChange" event on <input type="radio" onChange="">,...
13
by: Tim H | last post by:
Goal: to provide the simplest call-site possible at the cost of upfront hackery. I have a function that essentially takes a variable list of arguments: func(const string &k0, unsigned long...
6
by: antianti | last post by:
Hello, I have a function that has a variable # of arguments of varying types. int foo(short a, ...) { va_list argp va_start(argp, a); ... va_end(argp);
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.