va_arg and short

  • Thread starter Glen Herrmannsfeldt
  • Start date
J

James Kuyper

Brian said:
Peter said:
I wonder why va_arg could not accept a type subject to promotion and
handle them accordingly, i.e. accept the promoted type implicitly and
convert it to the original/requested type. [So, cases like the
character types and unsigned short, which might promote to signed or
unsigned int, could be handled without having to resort to integer
limit checks.]

While the programmer should know that the type char and short are
promoted, what about int32_t? Can it be an argument of va_arg? It
might be equivalent to a short (needing promotion), or a int or a long.
Is there any way of portably accessing a variadic int32_t (or intX_t for
X >= 16) parameter?

A long is guaranteed to hold at least the same values as an
int32_t, int and short may not. A short is guaranteed to hold at
least the same values as an int16_t. A signed char is guaranteed
to hold at least the same values as an int8_t. There are no other
guarantees AFAIR.

That means that the only signed integral types you can be certain won't
be promoted to 'int' are 'long' and 'long long', and similarly for
unsigned types. That causes a lot of portability problems when trying to
pass the size-named types to a variadic function. It also causes similar
problems when trying to pass them to functions declared without a
prototype, but that's easy to fix: just provide a prototype.
 
J

James Kuyper

[Cross posted to comp.std.c.] ....
I wonder why va_arg could not accept a type subject to promotion and
handle them accordingly, i.e. accept the promoted type implicitly and
convert it to the original/requested type. [So, cases like the
character types and unsigned short, which might promote to signed or
unsigned int, could be handled without having to resort to integer
limit checks.]

What sorts of architectures would make such an alternative va_arg
behaviour difficult to implement in practice?

The normal va_arg() macro can, on many platforms, expand to normal C
code. On some platforms, something like the following is sufficient:

typedef char* va_list;
#define va_start(arg) )(&arg+sizeof(arg))

#define va_start(ap,arg) ((ap) = (char *) &arg) /* ? */
#define va_arg(ap,type) ((type*)(ap+=sizeof(type)))[-1]
#define va_end(ap)

Try to figure out how to modify that without using any extensions to C
such as sizeof(promoted_typeof(type)). An implementation is free, of
course, to use extensions, but I think that the committee would resist
changing the specification in a way that would make the use of an
extension mandatory.

The 'extension' is static type analysis, indeed, the *same* analysis
used to promote parameters in variadic function calls in the first
place.

You'll have to explain that to me. Note: the above C code contains the
full definition of the <stdarg.h> for that platform; no C compiler
magic is needed, no special handling of code that contains va_arg().
It simply expands to C code which is interpreted in exactly the same
fashion that it would be if it were written by the user. The only
thing unportable about it is the assumptions it embodies about the
argument passing conventions.

Are you saying that static type analysis will somehow make the above
definitions work properly when given the original type of the
argument, rather than the promoted type? I don't see how that could
be: va_arg(ap,short) would seem to be incrementing ap by the wrong
amount, and using an lvalue with the wrong type to retrieve the value
it points at. I don't see how static type analysis can handle that.

Or are you saying that the above definitions can be re-written without
using implementation-specific extensions (of the syntax kind, not the
"static type analysis" kind) or compiler magic to produce the right
result? If so, could you give use the re-write?
 
J

James Kuyper

Oops! I missed one item; I should have included it in my previous
message:

(e-mail address removed) (James Kuyper) wrote in message news:<[email protected]>... ....

#define va_start(ap,arg) ((ap) = (char *) &arg) /* ? */

Sorry, that got messed up somehow, but your replacement is not the
correct fix. It should have been:

#define va_start(arg) ((char*)(&arg+sizeof(arg))
 
D

Dave Hansen

Oops! I missed one item; I should have included it in my previous
message:



Sorry, that got messed up somehow, but your replacement is not the
correct fix. It should have been:

#define va_start(arg) ((char*)(&arg+sizeof(arg))

?

Do you mean ((char*)(&arg+1))?

Or perhaps (((char*)&arg)+sizeof(arg))?

Regards,

-=Dave
 
G

Glen Herrmannsfeldt

Dan Pop said:
In <DJCpb.99053$Fm2.81929@attbi_s04> "Glen Herrmannsfeldt" programmers.

A non-C programmer has no business using a C compiler or building and
installing C programs. Imagine that the compiler emits a warning
(something that the person who wrote the code cannot avoid, for *any* C
compiler). Without knowing C, how can you tell if the problem can be
safely ignored, or the resulting executable should not be used?

A good argument for the compile once, run anywhere system that Java uses.

Though some programs supply a set of test inputs and expected outputs
carefully selected to test as much of the program as possible. The only one
I can think of right now is TeX, though there are likely others.
Even required diagnostics can be produced as "warnings" instead of
"errors", so the absence of compiler errors means exactly nothing.

And some systems will let you link and run a program, even with compiler
errors.
When the code is obviously broken, an error is much better. Far too many
people blissfully ignore the warnings...
Another argument for having a C programmer building C programs. Similar
issues arise with -lcurses vs -lncurses at link time. Again obvious for
the C programmer and totally insurmountable for the non-C programmer.

Well, yes. Though different amounts of experience will be necessary for
different problems.

-- glen
 
D

Douglas A. Gwyn

Dan said:
If I multiply two size_t values, do I get undefined behaviour in case of
overflow or the result modulo SIZE_MAX? It depends on whether size_t is
subject to the integral promotions or not, an issue the standard is
not addressing at all.

If size_t is being used properly, the question should never arise.
 
D

Daniel Haude

On Tue, 04 Nov 2003 17:09:51 -0500,
Douglas A. Gwyn said:
If size_t is being used properly, the question should never arise.

What do you mean by "used properly", and how does this proper usage avoid
the question of overflow?

--Daniel
 
D

Daniel Haude

On 4 Nov 2003 15:33:56 GMT,
in Msg. said:
A non-C programmer has no business using a C compiler or building and
installing C programs.

This is getting off-topic.

A normal computer user has plenty of business installing any kind of
programs though. There might be a C compiler under the hood of --say-- the
popular GNU "./configure && make && su root make install" mechanism, or
there might be some "Windows InstallShield" program at work. Both
approaches work fine most of the time, and, in my experience, both have
plenty of potential of installing broken an incomplete software, or of not
working at all. What difference does it make to a non-C-programmer? In
both cases, some obscure piece of software just fucks up.

(in the case of the C source code distribution, however, the C programmer
has a chance to have a go at the underlying problem which usually leads to
unjustifiable amounts of time spent on fixing it.)

--Daniel
 
P

Peter Nilsson

James Kuyper said:
(e-mail address removed) (Peter Nilsson) wrote in message

Almost:

#if INT32_MAX <= INT_MAX && INT32_MIN >= INT_MIN

I thought about that, but same signed types cannot overlap ranges. So even
taking your argument that INT_MAX need not be one less than a power of two
into acount, a programmer should only need to check one bound.

Unless I'm missing something?
 
M

Mantorok Redgormor

It's a clear case of undefined behaviour:

The va_arg macro expands to an expression that has the type and
value of the next argument in the call. The parameter ap shall be the
same as the va_list ap initialized by va_start. Each invocation of
va_arg modifies ap so that the values of successive arguments are
returned in turn. The parameter type is a type name specified such
that the type of a pointer to an object that has the specified type
can be obtained simply by postfixing a * to type. If there is no
actual next argument, or if type is not compatible with the type of
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
the actual next argument (as promoted according to the default
^^^^^^^^^^^^^^^^^^^^^^^^^=====================================
argument promotions), the behavior is undefined.
====================^^^^^^^^^^^^^^^^^^^^^^^^^^^

A short argument is promoted to int, therefore, by specifying short as the
type you're guaranteed to have a type mismatch, leading to undefined
behaviour. The compiler that rejected it just did you a favour. The code
was written by an incompetent programmer.

Dan

so to get the effect of a short, you would do something like this?

(short) va_arg(ap, int);



- nethlek
 
P

Peter Nilsson

James Kuyper said:
(e-mail address removed) (Peter Nilsson) wrote in message
(e-mail address removed) (James Kuyper) wrote in message
(e-mail address removed) (Peter Nilsson) wrote in message
[Cross posted to comp.std.c.] ...
I wonder why va_arg could not accept a type subject to promotion
and handle them accordingly, i.e. accept the promoted type
implicitly and convert it to the original/requested type. [So,
cases like the character types and unsigned short, which might
promote to signed or unsigned int, could be handled without
having to resort to integer limit checks.]

What sorts of architectures would make such an alternative va_arg
behaviour difficult to implement in practice?

The normal va_arg() macro can, on many platforms, expand to normal C
code. On some platforms, something like the following is sufficient:

typedef char* va_list; ....
#define va_arg(ap,type) ((type*)(ap+=sizeof(type)))[-1]

Try to figure out how to modify that without using any extensions to C
such as sizeof(promoted_typeof(type)).

I admit, that would be difficult.
You'll have to explain that to me.

My point is that the (or an) extension seems to be easy, and from there,
va_arg is also easy.
Note: the above C code contains the
full definition of the <stdarg.h> for that platform; no C compiler
magic is needed, no special handling of code that contains va_arg().
It simply expands to C code which is interpreted in exactly the same
fashion that it would be if it were written by the user. The only
thing unportable about it is the assumptions it embodies about the
argument passing conventions.

The standard(s) did add mandatory extension requirements for things like
setjmp. [Even floating point type support was a non-trivial mandatory
'extension' for implementations on chipsets without FPUs in 1989.] I just
don't see how an extension to accomodate an alternative va_arg() which
accepts promotable types would be a significantly greater burdon.

I guess it's a question of degrees. [I still haven't heard if the concept
was even raised for C89 or C99.]
 
P

Peter Nilsson

Brain fade on my part.
?

Do you mean ((char*)(&arg+1))?

Or perhaps (((char*)&arg)+sizeof(arg))?

That looks like it, only with the extra ap...

#define va_start(ap,arg) ((ap) = ((char *) &arg + sizeof(arg)))
 
P

Peter Nilsson

Christian Bau said:
[Cross posted to comp.std.c.]

Glen Herrmannsfeldt said:
I was compiling a program written by someone else about six years ago, and
widely distributed at the time. It also includes makefiles for many
different systems, so I know it has been compiled with many different
compilers.

I got compile errors when it used va_arg to fetch an argument of type
short.
That seemed a little strange to me, so I changed it to int and it compiled
just fine.

So now I wonder, just what is the rule for va_arg and short? It would
seem
strange to reject certain types, yet that is what the compiler did.

I wonder why va_arg could not accept a type subject to promotion and
handle them accordingly, i.e. accept the promoted type implicitly and
convert it to the original/requested type. [So, cases like the
character types and unsigned short, which might promote to signed or
unsigned int, could be handled without having to resort to integer
limit checks.]

These are no problem anyway, because the C Standard explicitely allows
you to use for example unsigned int instead of int if the value passed
can be represented in both types. So whether unsigned short is passed as
int or unsigned int, you can always read it as an unsigned int.

I see that in C99. Is it also in the previous standard? [It doesn't appear
to be in my copy of the C89 draft.]

In any case, plain char's signedness still causes a problem.
 
P

Peter Nilsson

Peter Nilsson said:
Christian Bau said:
(e-mail address removed) (Peter Nilsson) wrote: ....
I wonder why va_arg could not accept a type subject to promotion and
handle them accordingly, i.e. accept the promoted type implicitly and
convert it to the original/requested type. [So, cases like the
character types and unsigned short, which might promote to signed or
unsigned int, could be handled without having to resort to integer
limit checks.]

These are no problem anyway, because the C Standard explicitely allows
you to use for example unsigned int instead of int if the value passed
can be represented in both types. So whether unsigned short is passed as
int or unsigned int, you can always read it as an unsigned int.

I see that in C99. Is it also in the previous standard? [It doesn't appear
to be in my copy of the C89 draft.]

In any case, plain char's signedness still causes a problem.

Include float in the list too.
 
D

Dan Pop

In said:
Well, yes. Though different amounts of experience will be necessary for
different problems.

However, even the not-so-experienced C programmer will have a starting
point for investigating the issue, while the non-C programmer will be
completely lost. We've seen that many times in c.l.c, when non-C
programmers experienced a *trivial* program build problem and asked here
for help.

Dan
 
D

Dan Pop

In said:
On 4 Nov 2003 15:33:56 GMT,


This is getting off-topic.

A normal computer user has plenty of business installing any kind of
programs though.

Only binary distributions, validated for his platform.
There might be a C compiler under the hood of --say-- the
popular GNU "./configure && make && su root make install" mechanism, or
there might be some "Windows InstallShield" program at work.

Both require administrator privileges, which the normal computer user is
not supposed to have. In practice, there are plenty of normal users that
do have administrator provileges, and this explains why many machines get
screwed up and reinstalled from scratch far more often than others.

Dan
 
J

James Kuyper

Peter said:
I thought about that, but same signed types cannot overlap ranges.

Every arithmetic type has a range that overlaps the range of any other
arithmetic type, at the very least in the range from 0 to 127,
inclusive. Therefore, I presume that what you meant is not quite what
you said.

I presume that you're actually trying to refer to 6.2.5p8: " For any two
integer types with the same signedness and different integer conversion
rank (see 6.3.1.1), the range of values of the type with smaller integer
conversion rank is a subrange of the values of the other type."

Even so, it's still possible to have two types with the same upper limit
and different lower limits, or vice versa; that still makes the smaller
range a sub-range of the first one. In particular, it's not implausible
that an implementation might provide both 2's complement and 1's
complement types of the same size (one of which might need to be
emulated).
 
J

James Kuyper

On 4 Nov 2003 11:10:30 -0800, (e-mail address removed) (James Kuyper) wrote: ....

?

Do you mean ((char*)(&arg+1))?

Or perhaps (((char*)&arg)+sizeof(arg))?

Either one will work, of course, with brevity favoring the first. I
should have been paying closer attention. Sorry!

I've spent nearly two weeks wrestling with a bug that causes a SIGSEGV
inside of malloc(), and which disappears when I link to a debugging
version of the malloc() family. I finally got somewhere when I wrote
my own debugging version of the malloc() family, but I'm feeling more
than a little frazzled lately. :-(
 

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,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top