[comp.lang.c] Need help in <varargs.h>

tsai@Veritas.COM (Cary Tsai) (06/13/91)

Could you C guru tell me why the third ooprint() stmt causes coredump.
I must be missing something in ooprint().
Compile and execute 'a.out'. You'll get 'flag is on', 'flag is off' and
...(coredump) or any bus error message.

/*	help ....
*/
#include <stdio.h>
#include <varargs.h>

typedef	void	(*PFV)();

void ooprint(va_alist)
va_dcl
{
	va_list		ap;
	PFV		func;
	char		*client;
	char		*foo;

	va_start(ap);
	foo = va_arg(ap, char *);
	if ((func = va_arg(ap, PFV)) != (PFV) 0) {
		if ((client = va_arg(ap, char *)) != (char *) 0)
			func(client);
	}
	va_end(ap);
}

void testFunc1(testData)
char	*testData;
{
	if (testData && *testData)
		puts(testData);
}

void testFunc2(testData)
char	*testData;
{
	if (testData && *testData)
		puts(testData);
}

main()
{
	static	char	*mess1 = "flag is on";
	static  char	*mess2 = "flag is off";

	ooprint("ooprint", testFunc1, mess1);
	ooprint("ooprint", testFunc2, mess2);
	ooprint("ooprint"); /* WHY THIS STMT CAUSES CORE-DUMP? */
}

/* end of help ... */

You help and information would be appreciated.

willcr@bud.sos.ivy.isc.com (Will Crowder) (06/14/91)

In article <1991Jun12.174326.5390@Veritas.COM>, tsai@Veritas.COM (Cary Tsai)
writes:

|> Could you C guru tell me why the third ooprint() stmt causes coredump.
|> I must be missing something in ooprint().
|> Compile and execute 'a.out'. You'll get 'flag is on', 'flag is off' and
|> ...(coredump) or any bus error message.
|> 
|> [most of the source deleted]
|> 
|> 	va_start(ap);
|> 	foo = va_arg(ap, char *);
|> 	if ((func = va_arg(ap, PFV)) != (PFV) 0) {
|> 		if ((client = va_arg(ap, char *)) != (char *) 0)

It looks like you're expecting va_arg to return NULL if there is no argument.
This isn't how va_arg() works.  REMEMBER: In C, the called function has
no idea how many arguments you passed!  It generally has no way of
knowing, but that's not important.  The point is, as far as the language
definition is concerned, IT DOESN'T KNOW.

You must use either a sentinel value, an explicit argument count, or
some other method (such as counting the number of '%' in a printf() format
string) to determine how many times you can safely call va_arg() or use
the result.

I suspect what is happening is you are getting non-NULL garbage data from
your 

   ...func = va_arg(ap, PFV)...

and then garbage non-NULL data from

   ...client = va_arg(ap, char *)...

and then executing the garbage.

If you were to call it as

    ooprint("ooprint", (PFV) 0);

the third time it should work.

Hope this helps,

Will

P.S. (See the FAQ!)

--------------------------------------------------------------------------------
Will Crowder, MTS            | "That was setting #1.  Anyone want to see
(willcr@ivy.isc.com)         |  setting #2?"
INTERACTIVE Systems Corp.    |		-- Guinan

wolfram@messua.informatik.rwth-aachen.de (Wolfram Roesler) (06/14/91)

tsai@Veritas.COM (Cary Tsai) writes:

>void ooprint(va_alist)
>va_dcl
>{
>	va_start(ap);
>	foo = va_arg(ap, char *);
>	if ((func = va_arg(ap, PFV)) != (PFV) 0) {
>		if ((client = va_arg(ap, char *)) != (char *) 0)
>			func(client);

... so you see you always call va_arg at least twice...

>	ooprint("ooprint"); /* WHY THIS STMT CAUSES CORE-DUMP? */

... but here you pass only one argument. So the 2nd call to va_arg returns
a random value. Since this is likely to be !=0, you call va_arg the 3rd
time, which gives you a random value again, and then call what you received
in the 2nd call as a function. But, a random value is very unlikely to be
a valid function adress... So why should it do anything except a core dump?

The problem is that a varargs function can never determine how many args were
passed to it. You may pass only one arg and use va_arg to retrieve more than
one, but you will get garbage and _not_ null pointers after the arg list
is exhausted.

BTW: to improve style, use /*VARARGS*/ in front of the function header, this
helps using lint on this function.

Greetings
\/\/olfram

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/15/91)

In article <1991Jun12.174326.5390@Veritas.COM>, tsai@Veritas.COM (Cary Tsai) writes:
> Could you C guru tell me why the third ooprint() stmt causes coredump.
> I must be missing something in ooprint().
> void ooprint(va_alist)
> va_dcl
> {
> 	va_list		ap;
> 	PFV		func;
> 	char		*client;
> 	char		*foo;
> 
> 	va_start(ap);
> 	foo = va_arg(ap, char *);
> 	if ((func = va_arg(ap, PFV)) != (PFV) 0) {
	            ^^^^^^
Note: this function ALWAYS tries to access at least two arguments.

> 	ooprint("ooprint", testFunc1, mess1);
> 	ooprint("ooprint", testFunc2, mess2);
> 	ooprint("ooprint"); /* WHY THIS STMT CAUSES CORE-DUMP? */

Why _does_ that statement cause a core dump?
Well, WHERE IS THE SECOND ARGUMENT?
Failing to supply an argument is not the same as supplying a NULL.
I think you mean
	ooprint("ooprint", (PFV)0);

If the function really is as you have presented it, I don't see why
you're bothering with varargs at all.  It would be simpler just to
pass three arguments every time, and to write the 3rd call as
	ooprint("ooprint", (PFV)0, "");
Better type checking, no worries about varargs -vs- stdargs, &c &c &c.
-- 
Q:  What should I know about quicksort?   A:  That it is *slow*.
Q:  When should I use it?  A:  When you have only 256 words of main storage.