[va_list *] Is this legal ?

S

Shao Miller

typedef struct {
va_list ap;
} va_list_ex_t;

Above struct is wrong in case va_list for example is declared as:
typedef char va_list [1];

In this case there will be segfault during memcpy.

Fix for that:
typedef union {
va_list ap;
va_list * ptr_to_ap;
void * ptr_general;
} va_list_ex_t;

I don't understand why you need to wrap 'va_list' at all, when you can
use 'va_list *' parameters for all functions that are called by 'bar'.
 
M

m.labanowicz

void fooB(va_list ap) {
Did you mean 'va_list' type is not portable, or did you really mean

'va_list *' is not portable?
void fooB(va_list * pap) {
....

Please imagine that I'm implementator of function fooB that
declaration can not be modified:

void fooB(va_list ap);
 
S

Shao Miller

...

Please imagine that I'm implementator of function fooB that
declaration can not be modified:

void fooB(va_list ap);

Aha. I sort of figured that. :)

In that case, you can use 'va_copy', which is a portable macro. Then
you can pass around a pointer to that copy!

int get_int(va_list * pap) {
return va_arg(*pap, int);
}

void fooB(va_list ap) {
va_list ap_copy;

va_copy(ap_copy, ap);
get_int(&ap_copy);
va_end(ap_copy);
}
 
M

m.labanowicz

In that case, you can use 'va_copy', which is a portable macro. Then

Yes, it is true, but:

$gcc -W -Wall -ansi -pedantic test4.c
test4.c:43:3: warning: implicit declaration of function ‘va_copy’ [-Wimplicit-function-declaration]
/tmp/ccwjg0Bw.o: In function `bar':
test4.c:(.text+0x2cf): undefined reference to `va_copy'

'va_copy' is available from C99.

I'm looking solution for C89.
 
S

Shao Miller

In that case, you can use 'va_copy', which is a portable macro. Then

Yes, it is true, but:

$gcc -W -Wall -ansi -pedantic test4.c
test4.c:43:3: warning: implicit declaration of function ‘va_copy’ [-Wimplicit-function-declaration]
/tmp/ccwjg0Bw.o: In function `bar':
test4.c:(.text+0x2cf): undefined reference to `va_copy'

'va_copy' is available from C99.

I'm looking solution for C89.

C89 doesn't allow for a function with a 'va_list' parameter to pass that
parameter to yet another function. It also doesn't specify anything
about the 'va_list' type other than that it's a "type suitable for
holding information need by the macros va_start, va_arg, va_end." It is
also known to be an object type, because it states that 'ap' is an
object. But because it could be an array type, a 'va_list' parameter
could be either the same type as a 'va_list' declared outside of a
function parameter list, or it could be a different type.

The biggest problem I see with trying to make a pre-C89 'va_copy' or
with trying to code a work-around so that you can work with 'ap' at any
depth of call is that 'va_list' could be a const-qualified type, so you
can't portably modify it.

Having said that, I don't yet know if it's impossible. :)

Is it really a major pain to do all your 'va_arg' work in the function
with the 'va_list' parameter?
 
S

Shao Miller

In that case, you can use 'va_copy', which is a portable macro. Then

Yes, it is true, but:

$gcc -W -Wall -ansi -pedantic test4.c
test4.c:43:3: warning: implicit declaration of function ‘va_copy’
[-Wimplicit-function-declaration]
/tmp/ccwjg0Bw.o: In function `bar':
test4.c:(.text+0x2cf): undefined reference to `va_copy'

'va_copy' is available from C99.

I'm looking solution for C89.

C89 doesn't allow for a function with a 'va_list' parameter to pass that
parameter to yet another function. It also doesn't specify anything
about the 'va_list' type other than that it's a "type suitable for
holding information need by the macros va_start, va_arg, va_end." It is
also known to be an object type, because it states that 'ap' is an
object. But because it could be an array type, a 'va_list' parameter
could be either the same type as a 'va_list' declared outside of a
function parameter list, or it could be a different type.

The biggest problem I see with trying to make a pre-C89 'va_copy' or
with trying to code a work-around so that you can work with 'ap' at any
depth of call is that 'va_list' could be a const-qualified type, so you
can't portably modify it.

Having said that, I don't yet know if it's impossible. :)

Is it really a major pain to do all your 'va_arg' work in the function
with the 'va_list' parameter?

Just for fun, if you can rely on 'va_list' being a type that's not
const-qualified, you could experiment with the following code:

#include <stddef.h>
#include <stdarg.h>
#ifdef USE_MEMCPY
#include <string.h>
#endif

#define DECL_VA_BACKUP(backup, ap) \
unsigned char \
(backup)[sizeof (ap)], \
(backup ## _) = va_backup( \
(backup), \
(const void *) &(ap), \
(backup) + sizeof (ap), \
&(backup ## _) \
)
#define VA_BACKUP(backup, ap) \
((void) va_backup( \
(backup), \
(const void *) &(ap), \
(backup) + sizeof (ap) \
))
#define VA_RESTORE(ap, backup) \
((void) va_backup( \
(void *) &(ap), \
(backup), \
(void *) (&(ap) + 1) \
))

typedef unsigned char va_copy_t;

unsigned char va_backup(
va_copy_t * dest,
const va_copy_t * src,
va_copy_t * const dest_end,
...
) {
#ifndef USE_MEMCPY
while (dest < dest_end)
*dest++ = *src++;
#else
memcpy(dest, src, dest_end - dest);
#endif
return 0;
}

#include <stdio.h>
#include <stdarg.h>

int va_int(va_list ap, va_copy_t * ap_copy) {
int i;

VA_RESTORE(ap, ap_copy);
i = va_arg(ap, int);
VA_BACKUP(ap_copy, ap);
return i;
}

double va_double(va_list ap, va_copy_t * ap_copy) {
double d;

VA_RESTORE(ap, ap_copy);
d = va_arg(ap, double);
VA_BACKUP(ap_copy, ap);
return d;
}

void foo(va_list ap) {
DECL_VA_BACKUP(ap_copy, ap);
int i;
double d;

i = va_int(ap, ap_copy);
d = va_double(ap, ap_copy);
printf("i: %d, d: %f\n", i, d);
}

void caller(int parmN, ...) {
va_list ap;

va_start(ap, parmN);
foo(ap);
va_end(ap);
}

int main(void) {
caller(0, 42, 3.14159);
return 0;
}
 
M

m.labanowicz

Just for fun, if you can rely on 'va_list' being a type that's not
const-qualified, you could experiment with the following code:



#include <stddef.h>

#include <stdarg.h>

#ifdef USE_MEMCPY
....

Yes, it works.

This is similar solution that has been presented previously.

Regards
 
S

Shao Miller

...

Yes, it works.

This is similar solution that has been presented previously.

Right. And here's another just-for-fun (with the same limitation)
that's a bit simpler. Obviously, the macros should only be used by
functions that are more than one level deep from the first function
having a 'va_list' as a parameter. (Deeper than 'foo', in this case.)

#include <stddef.h>
#include <stdarg.h>
#ifdef USE_MEMCPY
#include <string.h>
#endif

#define VA_COMMIT(addr, ap) \
((void) va_backup( \
(addr), \
(const void *) &(ap), \
(unsigned char *) (addr) + sizeof (ap) \
))
#define VA_RESTORE(ap, addr) \
((void) va_backup( \
(void *) &(ap), \
(addr), \
(void *) (&(ap) + 1) \
))

typedef void * va_laddr;

unsigned char va_backup(
unsigned char * dest,
const unsigned char * src,
unsigned char * const dest_end,
...
) {
#ifndef USE_MEMCPY
while (dest < dest_end)
*dest++ = *src++;
#else
memcpy(dest, src, dest_end - dest);
#endif
return 0;
}

#include <stdio.h>
#include <stdarg.h>

int va_int(va_list ap, va_laddr pap) {
int i;

VA_RESTORE(ap, pap);
i = va_arg(ap, int);
VA_COMMIT(pap, ap);
return i;
}

double va_double(va_list ap, va_laddr pap) {
double d;

VA_RESTORE(ap, pap);
d = va_arg(ap, double);
VA_COMMIT(pap, ap);
return d;
}

void foo(va_list ap) {
int i;
double d;

i = va_int(ap, &ap);
d = va_double(ap, &ap);
printf("i: %d, d: %f\n", i, d);
}

void caller(int parmN, ...) {
va_list ap;

va_start(ap, parmN);
foo(ap);
va_end(ap);
}

int main(void) {
caller(0, 42, 3.14159);
return 0;
}

I don't think that the problem was really that using 'va_list *' wasn't
portable, it was that you weren't able to use it from the function with
the ellipsis. The problem with using it from a downstream function
seems to me to be the same as for any other function in C: You can't
access the caller's object without some sort of a reference to it.
Accessing the 'va_list' parameter in your 'fooB' is one step too late;
you want to access the original 'va_list' object in your 'bar'.
 
G

glen herrmannsfeldt

(snip)

(snip, then I wrote)
A number of the 64 bit processors do something like that, including
x86-64. Basically to avoid having to support full 64 bit addresses,
x86-64 only actually uses 48 bits of addresses but requires the high
nine bits of an address to all be the same. So effectively, there are
two chunks of valid addresses - 0x0000000000000000-0x007FFFFFFFFF and
0xFF80000000000000-0xFFFFFFFFFFFFFFFF.

48 bits should last for a while.

VAX and IBM S/370 both use a two level virtual addressing system.
IBM uses segment table and page tables, VAX uses pagable page tables.

IBM's 64 bit z/Architecture allows for five levels of tables, but with
special options to allow for fewer levels until more bits of address
are needed. Saves some time resolving address not in the TLB.

So, there might be some use for signed pointers.

-- glen
 
G

glen herrmannsfeldt

(snip, I wrote)
(and also wrote)
OTOH, the OS's for Z don't typically put the OS area at the top of the
64-bit address space. Parts exist near the 24 and 31 bit lines.

z/Linux probably just splits the space in two parts.

z/OS has to stay compatible with programs written over 40 years ago.
OS/360 stores other data in the high byte of 24 bit addresses in many
control blocks. When it had to fit in 20K bytes on a 64K byte machine,
that might have made sense. And z/OS doesn't use at all the space
between 2GB and 4GB, that is, between the 31 and 32 bit lines.

As I understand it, both the PL/I (F) compiler, and programs generated
by it, still run under z/OS.

-- glen
 
K

Keith Thompson

Keith Thompson said:
And if either intptr_t or uintptr_t provides a more natural mapping
to pointers on a given implementation, I see no way for a program
to detect that.
[...]

This program, whose behavior is blatantly undefined, might provide some
interesting information about how the compiler treats pointer values.

#include <stdio.h>
#include <stdint.h>
int main(void) {
if ((void*)(uintptr_t)-1 < (void*)(uintptr_t)0) {
puts("pointers are treated as signed");
}
else {
puts("pointers are treated as unsigned");
}
return 0;
}

(I get "pointers are treated as unsigned" on x86, x86_64, and SPARC.)
 
T

Tim Rentsch

[snip]
'va_list *' type can not be used (it is not portable).

This conclusion is exactly backwards. The most portable
choice you can make is to always use 'va_list *' (rather
than va_list) for any function parameter. It works
correctly across implementations, in every version of
standard C.
 
R

Roberto Waltman

glen said:
... The CDC machines use 60 bits, though I believe
that addresses don't use all the bits.

The 6600 had 18-bit addresses. (Size of A & B registers)
A char pointer had the word address in the lower 18 bits and an n-bit
field elsewhere to identify which byte in a word.
(To be accessed using mask and shift operations.)
 

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

No members online now.

Forum statistics

Threads
474,077
Messages
2,570,566
Members
47,202
Latest member
misc.

Latest Threads

Top