Path: utzoo!utgpu!water!watmath!clyde!att!osu-cis!tut.cis.ohio-state.edu!husc6!uwvax!oddjob!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c Subject: Re: Can ANYONE tell me why this code snippet doesn't work?? Message-ID: <14015@mimsy.UUCP> Date: 16 Oct 88 10:41:29 GMT References: <7778@gryphon.CTS.COM> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 126 In article <7778@gryphon.CTS.COM> rickf@pnet02.cts.com (Rick Flower) writes: >void Test(fmt,args) >char *fmt; >unsigned *args; >{ >char buff[129]; > > sprintf(buff,fmt,args); > printf("%s\n",buff); >} > >main() >{ > Test("%d %d %d %d",10,20,30,40); >} > I would think that it would just insert the 10 and 20 and 30 and >40 in the appropriate places in the fmt line.. but it only does the 1st two >numbers.. the other two are garbage values.. It is amazing that you get even the first *two* numbers (unless perhaps you are on a 16-bit-int 32-bit-pointer machine, such as a PDP-11 or IBM PC using large model). *Anyone* should be able to give you at least two reasons why it does not work: imprimis, you told sprintf to print four `int's (via %d), yet you passed it one pointer to unsigned; and secundus, Test() takes two arguments, the second being type `unsigned *', yet you passed five, the second being type `int'. You may not arbitrarily ignore either the types of arguments or the number of arguments to any function, save in one case: if the function being called is variadic. [Now that you have been thoroughly chastised :-) ...] The question now is how to write it so that it *will* work. Since what you want is a variadic function that acts much like printf, you must first declare a variadic function. There are two `standard' ways to do this, one the de facto standard, the other from the draft proposed ANSI standard for C. The two are not only different, they are also quite thoroughly incompatible---a mistake, as this was not really necessary (despite the cries of anguish from certain compiler-writers). Method 1. De facto standard. #include #include int my_printf(va_alist) va_dcl /* N.B.: no semicolon */ { int ret; char *fmt; va_list ap; char buf[129]; /* rather small, but might perhaps suffice */ va_start(ap); fmt = va_arg(ap, char *); ---> ret = (buf, fmt, ); va_end(ap); return (ret); } Everything except the line marked by the arrow is setup mechanics. The line with the arrow is the key to invoking *printf properly. So what goes in the and fields? ret = vsprintf(buf, fmt, ap); vsprintf is a version of sprintf that takes a `va_list' rather than an actual parameter list. It should now be obvious that sprintf itself could be written as int sprintf(va_alist) va_dcl { int ret; char *fmt, *buf; va_list ap; va_start(ap); buf = va_arg(ap, char *); fmt = va_arg(ap, char *); ret = vsprintf(buf, fmt, ap); va_end(ap); return (ret); } There are three assumptions about your system here, and one obvious one. The obvious one is that you have and can use the de facto standard varargs mechanism. The other assumptions are that you have vsprintf, and that you have a post-V7/4.3BSD sprintf that returns `int' rather than `char *'. Method 2: The dpANS standard. Method 2 is just like method one except that some of the names are changed, and some arguments are permitted and others required: #include #include int my_printf(char *fmt, ...) /* the `...'s are part of the syntax */ { int ret; va_list ap; char buf[128]; /* same comment as before */ va_start(ap, fmt); ret = vsprintf(buf, fmt, ap); va_end(ap); return (ret); } The `fmt' argument is both permitted and required: `...' may only appear after `,', and va_start must name the same parameter that appeared just before the `...'. If there were more fixed parameters, those, too, could be listed (so long as the va_start names the one closest to the three dots). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris