[comp.lang.c] variable number of strings passed to function - how?

pete@tutor.UUCP (Peter Schmitt) (10/20/88)

I want to write a function that accepts a variable number of string
arguments.  What is a possible way to do this?

-pete

-- 
           \\\!///		Peter Schmitt
            _   _ 		UUCP: decwrl!tsc.dnet!tutor!pete
          ( Q   Q )		        .or.  att!tsc!tutor!pete
---,,,,-------U-------,,,,---   ARPA: tutor!pete%tsc.dec.com@decwrl.dec.com

burris@ihuxz.ATT.COM (Burris) (10/21/88)

> I want to write a function that accepts a variable number of string
> arguments.  What is a possible way to do this?
> 
> -pete
> 

In the C language arguments are placed on the stack in reverse order
such that the first argument is the lowest index off of the stack pointer.
This allows you to pass the first argument as a count of the number of
following arguments.

Example:

	string_func( 3, "how", "what", "why" );
	
	or
	
	string_func( 4, "how", "what", "why", "where" );
	
	then
	
	int
	string_func( argn, argv )
	
	int	argn;
	char	*argv;
	{
		int	i;
		char	**argp;
		
		argp = &argv;		/* argp is a pointer to the first
					 * string pointer
					 */
		
		for( i = 0; i < argn; i++ )
		{
			printf( "%s\n", *argp++ );
		}
	}
	
Another (and probably better) way:

	string_func( "what", "why", "where", NULL );
	
	or
	
	string_func( "what", "why", "where", "how", NULL );
	
	then 
	
	int
	string_func( argv )
	
	char	*argv;
	
	{
		char	**argp;
		
		argp = &argv;

		/* now loop while the string pointer is not
		 * set to NULL
		 */
		
		while( *argp != NULL )
		{
			printf( "%s\n", *argp++ );
		}
	}

No, I didn't compile either example but they should give you a rough
idea of how to do this. The examples assume that the stack grows in a
negative direction.

In both cases you set argp to the ADDRESS of the first string pointer
on the stack. The CONTENTS of argp is the POINTER to the first string.
Incrementing argp causes it to point to the second string pointer.

Dave Burris
..att!ihuxz!burris

ariel@lznh.UUCP (<10000>Ariel Aloni) (10/22/88)

In article <3533@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>> I want to write a function that accepts a variable number of string
>> arguments.  What is a possible way to do this?
>> 
>> -pete
>> 
>
>In the C language arguments are placed on the stack in reverse order
                                                     ^^^^^^^^^^^^^^^^
[ stuff deleted ]
This is a convention adopted by most compilers, it was never promised
by K&R (maybe somebody can enlighten us on ANSI-C ?).

>No, I didn't compile either example but they should give you a rough
>idea of how to do this. The examples assume that the stack grows in a
                                                      ^^^^^^^^^^^^^^^^
>negative direction.
^^^^^^^^^^^^^^^^^^^
bad assumption -- e.g. 3B2/600 running SysVr3.1 is growing in a
positive direction. Again, stack growth is never defined, it depends
on the implementation.

to the point :
use either varargs (see man page) or you can always go to the
simple "main" conventions (either "argv" convention or "arge"
convention) in this case.

>Dave Burris
>..att!ihuxz!burris

ariel


-- 
Ariel A. Aloni					att!ctsmain!raphel!ariel
(201) 576-2937

rkl1@hound.UUCP (K.LAUX) (10/22/88)

In article <434@tutor.UUCP>, pete@tutor.UUCP (Peter Schmitt) writes:
> I want to write a function that accepts a variable number of string
> arguments.  What is a possible way to do this?
> 

	The best place to look is the source code for the printf () function.

	Also, the header file 'varargs.h' has some helpful defines for
writing just such functions.

	If you are using MSC 5.1, the reference manuals do describe and
explain how to.

--rkl

karl@haddock.ima.isc.com (Karl Heuer) (10/22/88)

In article <3533@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>In the C language arguments are placed on the stack in reverse order

This is not true in general.

>	string_func( "what", "why", "where", NULL );

In order for this to always work right, the NULL must be explicitly cast:
(char *)NULL or (char *)0 are the two correct ways to write it.  (Don't
followup to say you don't believe this. Ask Chris Torek (mimsy!chris) to mail
you the NULL article from his Frequently Asked Questions archive.)

>The examples assume that the stack grows in a negative direction.

If you write it with <stdarg.h>/<varargs.h>, you don't need to make that
assumption.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

chris@mimsy.UUCP (Chris Torek) (10/22/88)

In article <3533@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>In the C language arguments are placed on the stack in reverse order ....

In the C language there may or may not *be* a stack.

Pyramid's compiler places the first 12 (or so; it was 12 when we had one)
`simple' arguments in registers.  The machine can take the address of
registers, so the first 12 arguments are in fact contiguous at increasing
addresses.  The 13th and succeeding arguments, however, are at some other
location entirely (on the data stack---the registers are stored on the
control stack, when the current window overflows).

This is why you must use <varargs.h> (or <stdarg.h>).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

pardo@june.cs.washington.edu (David Keppel) (10/22/88)

>>[ I want to do string varargs ]

In article <3533@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>[ In C, args are put on the stack in reverse order so the first arg
>  is the lowest arg of the stack ponter ]
>[ example deleted ]

NO!  DON'T TOUCH THAT... <sound of muffled explosion>

This is true on *some* machines, but is certainly false on other
machines.  There are a couple reasons why.  One is that the C language
doesn't define things to be done this way, so anybody who wants to
push right-to-left can do so as long as their compiler works.  I'm not
sure that any modern compilers do this *currently*, but (a) there have
been in the past and (b) there probably will be again.  Another good
reason why this isn't portable is that some machines have stacks that
grow *up* and some have stacks that grow *down*.  Finally, there is no
guarantee that the arguments, even arguments of the same size, will be
in contiguous memory.

How to do this portably?  Start with <varargs.h> or <stdargs.h>.
Then go read <14015@mimsy.UUCP> (comp.lang.c, 16 Oct 1988).

	;-D on  ( "Every execution leaves you dangling..." )  Pardo
-- 
		    pardo@cs.washington.edu
    {rutgers,cornell,ucsd,ubc-cs,tektronix}!uw-beaver!june!pardo

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/23/88)

In article <434@tutor.UUCP> pete@tutor.UUCP (Peter Schmitt) writes:
>I want to write a function that accepts a variable number of string
>arguments.  What is a possible way to do this?

There are two portable ways to do this as stated, using variable-
argument facilities (you could also pass as a fixed argument a
variable-sized data structure containing the strings).  Either a
count of the number of strings is passed as the first argument,
or a null pointer (end marker) is passed after the last string.

The two standard variable-argument facilities are associated with
the headers <varargs.h> (most current implementations) and
<stdarg.h> (ANSI C conforming implementations).  I generally code
for both, using #if __STDC__ as necessary to control compilation
of the correct variation of the code.  You should refer to your
reference manual for details of these facilities, but here is an
example extracted from the BRL/VLD MUVES project source code:


#include <stdio.h>
#include <string.h>

#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#include <Er.h>			/* error-logging package interface */
#include <Mm.h>			/* memory-management package iface */

/*
	IoOpenFile() opens the file named by the concatenation of its
	variable character-string arguments for reading; it returns NULL
	if memory could not be allocated to build the pathname or if the
	file could not be opened (in which case ErIndex is set to
	`errnum').  The final argument must be (char *)NULL.
*/

#if __STDC__
FILE *
IoOpenFile( long errnum, ... )
	{
#else
/*VARARGS*/
FILE *
IoOpenFile( va_alist )
	va_dcl
	{
	long	errnum;
#endif
	va_list		ap;
	register FILE	*fp;		/* opened file stream pointer */
	register char	*p;		/* -> filename segment */
	register char	*cp;		/* -> end of partial filename */
	register int	name_len;	/* string allocation length */
	char		*fname;		/* full path name of file */

	/* allocate space for filename */

#if __STDC__
	va_start( ap, errnum );
#else
	va_start( ap );
	errnum = va_arg( ap, long );
#endif

	for ( name_len = 1; (p = va_arg( ap, char * )) != NULL; )
		name_len += strlen( p );

	va_end( ap );

	if ( (fname = cp = MmVAllo( name_len, char )) == NULL )
		return (FILE *)NULL;	/* ErIndex already set */

	/* construct concatenated filename */

#if __STDC__
	va_start( ap, errnum );
#else
	va_start( ap );
	(void)va_arg( ap, long );	/* skip errnum */
#endif

	while ( (p = va_arg( ap, char * )) != NULL )
		while ( (*cp = *p++) != '\0' )
			++cp;

	va_end( ap );

	/* try to open the file */

	if ( (fp = fopen( fname, "r" )) == NULL )
		ErSet( errnum );

	/* deallocate filename space */

	MmVFree( name_len, char, fname );
	return fp;			/* (may be NULL) */
	}

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/23/88)

In article <3533@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>In the C language arguments are placed on the stack in reverse order

NO! NO! NO!

Do not follow that fellow's advice if you are at all concerned about
your code working on a wide variety of C implementations.

What he said may apply to HIS particular machine but definitely is
UNTRUE in general.

guy@auspex.UUCP (Guy Harris) (10/24/88)

>In the C language arguments are placed on the stack in reverse order
>such that the first argument is the lowest index off of the stack pointer.

*AHEM*  No, in certain *implementations* of the C language arguments are
placed onto the stack in reverse order....

In other implementations some of them are stuffed into registers.

The closest things to a portable way of doing a function taking a
variable number of arguments are:

	1) "varargs" - present on many (most?) UNIX systems' C
	   implementations, and possibly on other implementations as
	   well (see VARARGS(n), for some value of "n", in the UNIX
	   documentation; if you don't have UNIX, look somewhere else).

	2) "stdargs" - specified by ANSI C, so probably present in many
	   microcomputer implementations.

burris@ihuxz.ATT.COM (Burris) (10/24/88)

OK folks, enough already!!!

In my attempt to be helpful in offering possible solutions to the
requested problem I have received a few very patronizing responses.

I tried to qualify my examples but obviously goofed by not being
more specific.

K & R pg. 186 copyright 1976 states,

"...The order of evaluation or arguments is undefined by the language,
take note that the various compilers differ."

I was showing examples based on my experience with approx. 10 different
C compilers on 4 different processors that all acted the way I stated.
This is not necessarily the way your compiler will handle things. It
is nonetheless an indication of how one might go about solving such
a problem.

I appreciate some of the responses that pointed out my omission of
possibly important facts but some of you folks could stand to come down
from your "high horse" long enough to take things in the nature they
were intended, as attempts to be helpful to someone asking for solutions
to problems they wish to solve.

My apologies for not being more specific!

Dave Burris
..!att!ihuxz!burris

henry@utzoo.uucp (Henry Spencer) (10/25/88)

In article <3533@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>> I want to write a function that accepts a variable number of string
>> arguments.  What is a possible way to do this?
>
>In the C language arguments are placed on the stack in reverse order...

No.  In some C *implementations* they are placed on the stack in reverse
order.  In others they are placed on the stack in non-reverse order.  In
others they are passed in registers.  In yet others, different types of
arguments are passed in different kinds of registers.  An implementation
which uses registers will usually have to pass really big arguments on
an in-memory stack anyway.  And so on.  Use <varargs.h> or <stdarg.h>
(current practice and ANSI-C-draft practise respectively); they are the
*only* portable way to do this.
-- 
The dream *IS* alive...         |    Henry Spencer at U of Toronto Zoology
but not at NASA.                |uunet!attcan!utzoo!henry henry@zoo.toronto.edu

joshua@athertn.Atherton.COM (Sleaze Hack) (10/25/88)

In article <1962@lznh.UUCP> ariel@lznh.UUCP (<10000>Ariel Aloni) writes:
>
>[Lots deleted, but we're talking about the order arguments get pushed on
> the stack during a C function call.]
>
>This is a convention adopted by most compilers, it was never promised
>by K&R (maybe somebody can enlighten us on ANSI-C ?).
>

Not only does ANSI not promise us the order of arguments pushed on the stack,
it does not promise us that this order will always be the same within a
program!  For example, the Tandem compiler (version C00 and later) pushes
arguments left to right, unless the function takes a variable number of
arguments, when it pushes them right to left.  The standard says it is OK
to force vararg functions to be prototyped before use, and Tandem requires
this, so that it can always tell the difference between an "normal" function
and a "vargargs" function.  Obviously, having two different, and incompatible,
stack frames in use in the same program can cause a lot of problems if you
are not very careful.  Think of all the problems that pointers to functions
could cause!

Tandem did this so that most function calls would be compatible with their
other languages (PASCAL, TAL, etc.) which push arguments left to right.
Tandem claims that this compiler is as ANSI conformant as possible
(realizing that the standard is not a Standard yet).

Summary on the portable use of variable arguments to a C function:
use stdargs or varargs, or somewhere you will die.

Josh
--------                Quote: "If you haven't ported your program, it's not
Addresses:                      a portable program.  No exceptions."  
joshua@atherton.com OR         
sun!athertn!joshua  OR                 
{backbone}!{decwrl!hpda}!athertn!joshua  work:(408)734-9822 home:(415)968-3718

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/25/88)

In article <3542@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>I appreciate some of the responses that pointed out my omission of
>possibly important facts but some of you folks could stand to come down
>from your "high horse" long enough to take things in the nature they
>were intended, as attempts to be helpful to someone asking for solutions
>to problems they wish to solve.

Probably the main reason you were flamed for your attempt to be helpful
is that the advice you gave was poor.  There is a well-defined mechanism
for dealing with variable function arguments, and users of the mechanism
do not have to worry about their code breaking in mysterious ways when
it is ported to a different architecture, as it would have had your
advice been followed.  Whenever a fairly convenient portable solution
to a problem exists, it should be used in preference to one that relies
on nonportable details of a particular implementation.

It is particularly galling to see such a suggestion coming out of AT&T,
considering all the hours I have spent in tracking down and fixing this
exact problem in source code that AT&T licensed commercially.  I hope
you AT&T hackers learn how to use the official variable argument
mechanism and save us all a lot of unnecessary grief.

pardo@june.cs.washington.edu (David Keppel) (10/26/88)

henry@utzoo.uucp (Henry Spencer) writes:
>[ arguments passed: forwards, backwards, registers, ... ]

This probably seems like a silly question, but are there any compilers
that pass arguments globally?  Things of the form

		static void
	foo (m, n)
		int m, n;
	{
		if (m==0)
			return;
		m = bletch (m,n);
		foo (m,n);
	}

Would win from this.  I doubt that these account for a
significant part of the usage.
-- 
		    pardo@cs.washington.edu
    {rutgers,cornell,ucsd,ubc-cs,tektronix}!uw-beaver!june!pardo

gwyn@smoke.BRL.MIL (Doug Gwyn ) (10/26/88)

In article <6199@june.cs.washington.edu> pardo@uw-june.UUCP (David Keppel) writes:
>This probably seems like a silly question, but are there any compilers
>that pass arguments globally?

In general, C semantics require that a function invocation's value
parameters be unique to the activation record.  They cannot be shared.

Your particular example would allow that optimization, IF all users of
the function were known to the compiler at the point that it was
deciding what form of function linkage to use for that function.
However, it is doubtful that the extra analysis and irregular linkage
mechanism would be worthwhile for such an unlikely case.

alfie@warwick.UUCP (Nick Holloway) (10/26/88)

In article <3533@ihuxz.ATT.COM> burris@ihuxz.ATT.COM (Burris) writes:
>In the C language arguments are placed on the stack in reverse order
>
Demonstration 1:
    main () { int i = 1; printf ( "%d %d %d\n", i++, i++, i++ ); }

Note: I _do_ know that this is bad code - but it does demonstrate the
      order of evaluation.

First I compiled and ran this on a sun3. The output is what most people 
expect from their compiler. (args evaluated from right to left)
    3 2 1
Then I compiled it on the sun4 here (sparc), and the results suprised me!
    2 1 3
Looking at the assembly, it was confusing, but it did seem rather long, 
so I compiled with -O, and this gave
    1 2 3

I think this is a good demonstration of why you must never depend on 
the order evaluation of arguments (On the sparc, even compile time 
flags make a difference). 

Demonstration 2:
    test (a,b) int a,b; 
	{ printf ( "&a=%u\n&b=%u\n", (unsigned)&a, (unsigned)&b ); }
    main () { test ( 1, 2 );}

On the sun3, the output was: (args at increasing addresses)
    &a=251657624
    &b=251657628
On the sun4: (args at decreasing addresses)
    &a=4160748428
    &b=4160748432

So we have concrete examples why you can never assume anything about the
calling convention if you wish your programs to be portable, and use a 
standard interface such as <varargs.h>/<stdargs.h>.

Note: I normally indent my programs, this is just to cut down on space in 
      this article. Also I did include <stdio.h>.
--
JANET : alfie@uk.ac.warwick.cs        | `O O' |16 Queens Rd|   /: :   :-- : :--
UUCP  : ..!mcvax!ukc!warwick!alfie    |// ^ \\|Hertford    |  / : :   :   : :
BITNET: alfie%uk.ac.warwick.cs@ukacrl --------|Herts       | /--: :   :-  : :-
ARPA  : alfie%cs.warwick.ac.uk@cunyvm.cuny.edu|England     |/   : :__ :   : :__

ron@motmpl.UUCP (Ron Widell) (10/30/88)

In article <787@ubu.warwick.UUCP> alfie@warwick.UUCP (Nick Holloway) writes:
[An otherwise good example of argument evaluation ordering]
=Demonstration 2:
=    test (a,b) int a,b; 
=	{ printf ( "&a=%u\n&b=%u\n", (unsigned)&a, (unsigned)&b ); }
=    main () { test ( 1, 2 );}
=
=On the sun3, the output was: (args at increasing addresses)
=    &a=251657624
=    &b=251657628
=On the sun4: (args at decreasing addresses)
		       ^^^^^^^^^^  Huh ?
=    &a=4160748428
=    &b=4160748432
I'm not sure what you mean here. It appears to me that the address of
b is higher than that of a, just like the sun3 (it's certainly a larger
number ;-)).

=JANET : alfie@uk.ac.warwick.cs        | `O O' |16 Queens Rd|   /: :   :-- : :--
=UUCP  : ..!mcvax!ukc!warwick!alfie    |// ^ \\|Hertford    |  / : :   :   : :
=BITNET: alfie%uk.ac.warwick.cs@ukacrl --------|Herts       | /--: :   :-  : :-
=ARPA  : alfie%cs.warwick.ac.uk@cunyvm.cuny.edu|England     |/   : :__ :   : :__


-- 
Ron Widell, Field Applications Eng.	|UUCP: motmpl!ron
Motorola Semiconductor Products, Inc.,	|Voice:(612)941-6800
9600 W. 76th St., Suite G		| If they *knew* what I was saying,
Eden Prairie, Mn. 55344 -3718		| do you think they'd let me say it?

crossgl@ingr.UUCP (Gordon Cross) (11/01/88)

In article <3533@ihuxz.ATT.COM>, burris@ihuxz.ATT.COM (Burris) writes:
> In the C language arguments are placed on the stack in reverse order
> such that the first argument is the lowest index off of the stack pointer.
> This allows you to pass the first argument as a count of the number of
> following arguments.
> 
> Example:

  [ example deleted to save space ]

> 
> In both cases you set argp to the ADDRESS of the first string pointer
> on the stack. The CONTENTS of argp is the POINTER to the first string.
> Incrementing argp causes it to point to the second string pointer.
> 

The proper way to access variable numbers of arguments passed to a routine is
to use use <varargs.h>!!  Any assumptions about stack layout is just asking
for trouble (for example some compilers pass the first couple of args in
registers for efficiency reasons).  Check your manuals or any good "C"
programming book on how to use these macros....


Gordon Cross
Intergraph Corp.  Huntsville, AL