What types are legal as (...) arguments?

S

Stephan Beal

Hello, all!

i'm working on a program where i'm making heavy use of (...) arguments
and i have a question about them. It's all working fine and well, but
i'm getting a warning from one compiler which makes the think that
perhaps my usage isn't portable, and i want to find out what C says
about my use case (rather than what my compilers say).

Consider:

struct my_struct {
int i;
struct my_struct * left;
struct some_struct bar;
};

Now, assuming i have a routine:

void do_mything( struct context *, ... );

i'm calling that like:

static const my_struct end; /* special argument to signal end of list
*/
my_struct foo;
my_struct bar;
.... initialize structs ...

do_mything( myContext, foo, bar, end );

My question is simple: is it legal (strictly speaking) to pass objects
other than built-in types here? gcc and SunStudio compilers don't
complain about it and the code words as expected, but tcc says
"warning: assignment of read-only location" (but it still works as
expected), so my suspicion is that gcc and SunCC are being too lenient
on me and that tcc is trying to tip me off to a violation of the
standard.

If it makes a difference, i'm trying to code for pre-C99, but i also
don't mind if i have to rely on C99 features.

To cut off the anticipated counter-question in advance: "why not pass
pointers to my_struct instead?" The answer is:

a) i do. i have two variants of my routines: one takes a list of
pointers and one takes a list of value types.
b) both approaches have slightly different implications in my library
and both are useful in different contexts.

:-?
 
K

Keith Thompson

Stephan Beal said:
Consider:

struct my_struct {
int i;
struct my_struct * left;
struct some_struct bar;
};

Now, assuming i have a routine:

void do_mything( struct context *, ... );

i'm calling that like:

static const my_struct end; /* special argument to signal end of list
*/
my_struct foo;
my_struct bar;
... initialize structs ...

do_mything( myContext, foo, bar, end );
[...]

It would be helpful if you posted more complete code. You haven't
shown us the declaration of my Context. Is it an object of type
struct context*?
 
S

Stephan Beal

It would be helpful if you posted more complete code. You haven't
shown us the declaration of my Context. Is it an object of type
struct context*?

That's irrelevant for this purpose - i'm interested in the (...) args.
In this example the context is only there so i have an argument to
pass to va_start(). The exact types used are not significant for this
example, either, other than to demonstrate that i'm passing structs of
arbitrary size *by value* via (...)/va_list, and i'm curious as to
whether doing so is strictly C (or C99) compliant or whether i'm
unintentionally relying on a compiler-specific extension.
 
J

jameskuyper

Stephan said:
Hello, all!

i'm working on a program where i'm making heavy use of (...) arguments
and i have a question about them. It's all working fine and well, but
i'm getting a warning from one compiler which makes the think that
perhaps my usage isn't portable, and i want to find out what C says
about my use case (rather than what my compilers say).

Consider:

struct my_struct {
int i;
struct my_struct * left;
struct some_struct bar;
};

Now, assuming i have a routine:

void do_mything( struct context *, ... );

i'm calling that like:

static const my_struct end; /* special argument to signal end of list
*/
my_struct foo;
my_struct bar;

In each of the above cases, you were supposed to have "struct
my_struct", not just plain "my_struct". Did you make a typedef you
haven't shown us, or are you're compiling this as C++ code, rather
than as C code? If this is C++, your question actually belongs on
comp.lang.c++, not here.
... initialize structs ...

do_mything( myContext, foo, bar, end );

My question is simple: is it legal (strictly speaking) to pass objects
other than built-in types here? gcc and SunStudio compilers don't
complain about it and the code words as expected, but tcc says
"warning: assignment of read-only location" (but it still works as
expected), so my suspicion is that gcc and SunCC are being too lenient
on me and that tcc is trying to tip me off to a violation of the
standard.

The code you've written does not seem to contain any "assignment of
read-only location". The only thing you've shown us that could be
stored in read-only memory is 'end', and your code reads that value,
it doesn't write it.

Therefore, I suspect that the problem may be due to some code that you
haven't shown us. Please provide a complete, compilable piece of code,
as simple as possible. Please confirm that tcc produces the same error
message on the simplified code as it does on your actual code.

The following is the simplest piece of code I could come up with,
consistent with what you've already written. Does tcc issue any
warning messages when compiling it?

#include <stdarg.h>
struct context {
int i;
};

struct some_struct {
int i;
};

struct my_struct {
int i;
struct my_struct * left;
struct some_struct bar;
};

void do_mything( struct context *, ... );

int main(void)
{
static const struct my_struct end;
/* special argument to signal end of list */

struct my_struct foo = {0};
struct my_struct bar = {0};
struct context actual = {0};
struct context *myContext = &actual;

do_mything( myContext, foo, bar, end );
return 0;
}

void do_mything( struct context *myContext, ... ){
va_list ap;
va_start(ap, myContext);
struct my_struct foo = va_arg(ap, struct my_struct);
struct my_struct bar = va_arg(ap, struct my_struct);
struct my_struct end = va_arg(ap, struct my_struct);
va_end(ap);
}
 
S

Stephan Beal

The following is the simplest piece of code I could come up with,
consistent with what you've already written. Does tcc issue any
warning messages when compiling it?

Yes, exactly. Let's see what happens:

stephan@jareth:~/cvs/fossil/pegc/src$ gcc -c foo.c
stephan@jareth:~/cvs/fossil/pegc/src$ tcc -c foo.c
foo.c:31: warning: assignment of read-only location
stephan@jareth:~/cvs/fossil/pegc/src$ sed -n 29,33p < foo.c
struct context *myContext = &actual;

--here--> do_mything( myContext, foo, bar, end );
return 0;

(Thanks for that example, by the way. i was about to go factor an
example out of my existing code.)

So it seems that this is a bogus warning, and not a violation of the C
standard?
 
K

Keith Thompson

Stephan Beal said:
That's irrelevant for this purpose - i'm interested in the (...) args.
In this example the context is only there so i have an argument to
pass to va_start(). The exact types used are not significant for this
example, either, other than to demonstrate that i'm passing structs of
arbitrary size *by value* via (...)/va_list, and i'm curious as to
whether doing so is strictly C (or C99) compliant or whether i'm
unintentionally relying on a compiler-specific extension.

Actual code is almost always relevant. The code you posted (a) is
incomplete, and (b) seems unlikely to trigger the warning message you
say you're seeing. It's possible that you've assumed that the warning
message is caused by your used of struct parameters in a variadic
function.

If you posted a complete self-contained program that produces the
warning, we'd have a much better chance of telling you why you're
getting the warning.

As far as the language (either C90 or C99) is concerned, there should
be no problem passing either pointers or structures as arguments to a
variadic function, as long as you invoke va_arg and friends properly
within the function. The only problems you can expect are from using
types that are promoted (which pointers and structures aren't). But
it's entirely possible that a compiler could have a bug in this area;
passing structures to variadic functions is a fairly unusual thing to
do.
 
S

Stephan Beal

Actual code is almost always relevant. The code you posted (a) is

You're right, and i appologize for not posting an example. My question
was not about the code, really, but more about about what limitations
the C standard may or may not prescribe for varargs.
As far as the language (either C90 or C99) is concerned, there should
be no problem passing either pointers or structures as arguments to a
variadic function, as long as you invoke va_arg and friends properly
within the function.

Thank you for that. That's EXACTLY the answer i was looking for.
The only problems you can expect are from using
types that are promoted (which pointers and structures aren't). But
it's entirely possible that a compiler could have a bug in this area;
passing structures to variadic functions is a fairly unusual thing to
do.

i hope to keep my va_list usage to a minimum (i find it too error-
prone
for the general case), so i hope i won't run into any tricky cases.

My many thanks for your time :). i will rest easier tonight with the
knowledge that i don't have to go remove the (...) bits from my API.
 
K

Keith Thompson

Stephan Beal said:
You're right, and i appologize for not posting an example. My question
was not about the code, really, but more about about what limitations
the C standard may or may not prescribe for varargs.


Thank you for that. That's EXACTLY the answer i was looking for.


i hope to keep my va_list usage to a minimum (i find it too error-
prone
for the general case), so i hope i won't run into any tricky cases.

My many thanks for your time :). i will rest easier tonight with the
knowledge that i don't have to go remove the (...) bits from my API.

I should warn you, though, that I recently made a serious error here
discussing variadic functions. (I assumed that the parameter
preceding the ",..." could be of any type; in fact, if it's of a type
that's affected by argument promotions, i.e., either float or an
integer type narrower than int, then the behavior is undefined.) I
*think* I've got it right this time, but I'm not certain. If nobody
posts a correction, I'll be more confident.
 
J

jameskuyper

Stephan said:
Yes, exactly. Let's see what happens:

stephan@jareth:~/cvs/fossil/pegc/src$ gcc -c foo.c
stephan@jareth:~/cvs/fossil/pegc/src$ tcc -c foo.c
foo.c:31: warning: assignment of read-only location
stephan@jareth:~/cvs/fossil/pegc/src$ sed -n 29,33p < foo.c
struct context *myContext = &actual;

--here--> do_mything( myContext, foo, bar, end );
return 0;

(Thanks for that example, by the way. i was about to go factor an
example out of my existing code.)

So it seems that this is a bogus warning, and not a violation of the C
standard?

I believe so. I can't see anything wrong with it, nor can I see any
meaningful connection between the warning message and the code that it
is supposed to be referring to.
 
B

Barry Schwarz

snip
I should warn you, though, that I recently made a serious error here
discussing variadic functions.  (I assumed that the parameter
preceding the ",..." could be of any type; in fact, if it's of a type
that's affected by argument promotions, i.e., either float or an
integer type narrower than int, then the behavior is undefined.)  I
*think* I've got it right this time, but I'm not certain.  If nobody
posts a correction, I'll be more confident.

This doesn't look right for function calls with a prototype in scope.
I have tried to see where this would be derived from 6.5.2.2 and there
doesn't appear to be a restriction on the parameters preceding the
ellipses. Nor are these parameters promoted.
 
B

Ben Bacarisse

Barry Schwarz said:
snip


This doesn't look right for function calls with a prototype in scope.
I have tried to see where this would be derived from 6.5.2.2 and there
doesn't appear to be a restriction on the parameters preceding the
ellipses. Nor are these parameters promoted.

The problem comes from:

7.15.1.4 The va_start macro

in which paragraph 4 reads:

4 The parameter parmN is the identifier of the rightmost parameter
in the variable parameter list in the function definition (the
one just before the , ...). If the parameter parmN is declared
with the register storage class, with a function or array type,
or with a type that is not compatible with the type that results
after application of the default argument promotions, the
behavior is undefined.

It is hardly surprising that Keith missed this[1] since it seems to be
a pointless extra condition. Section 6.5.2.2 says that in a call to a
vararg function with a prototype in scope, the initial parameters are
converted (as if by assignment) to the declared types, and (in another
paragraph) that a call to a vararg function without a prototype in
scope is UB. Thus the whole call is either UB or there is prototype
in scope that precludes any need to apply the default argument
promotions.

I can only assume that the intent is to permit implementations to
apply the default argument promotions on all arguments to a vararg
function call. Maybe that is the only way to arrange such calls and
the stdarg.h macros on some obscure architecture?

[1] I missed it too but that is way less surprising and, consequently,
far less noteworthy.
 
O

Old Wolf

I believe so. I can't see anything wrong with it, nor can I see any
meaningful connection between the warning message and the code that it
is supposed to be referring to.

I would be suspicious as to whether the compiler
is actually generating correct code, given that it
seems to not be handling the source code properly.
 
N

Nate Eldredge

Old Wolf said:
I would be suspicious as to whether the compiler
is actually generating correct code, given that it
seems to not be handling the source code properly.

It is. I checked.
 

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

Members online

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top