chin@ankh.ftl.fl.us (Albert Chin) (06/07/90)
How does printf() work. I believe there is some way in C to obtain the pointer to the argument list in a function. If so, printf() is pretty simple. But how do you do this. Is it possible to get the number of arguments in a printf() statement? albert chin ... mthvax!mamia!albert
dankg@tornado.Berkeley.EDU (Dan KoGai) (06/08/90)
In article <353@ankh.ftl.fl.us> chin@ankh.ftl.fl.us (Albert Chin) writes: >How does printf() work. I believe there is some way in C to obtain the >pointer to the argument list in a function. If so, printf() is pretty >simple. But how do you do this. Is it possible to get the number of >arguments in a printf() statement? On C convention, arguments are pushed to the stack right to left order before it jumps to the function. Since you can push as many arguments as your memory spec allows, it's quite possible, as you see on printf, to make variable argument functions on C. However, variable argument functions are somewhat kludgy on C: Bad news is that you have to explicity include the number of arguments somewhere in your argument and there's no other way of getting number of arguments: in printf() and scanf(), et al., It's number of '%'s in format and it might crash if you give wrong number of arguments. Also note there's no way you can tell the type of arguments in variable arg functions--printf() uses format to specify it and even though you feed wrong type args in printf() it uses argument type implied in format--it's a common mistake to pass int, instead of pointer to int in scanf(). On Ansi C, you can use <stdarg.h> macro package to write variable argument function. And variable argument is implied by "...". Here's simple example: #include <stdarg.h> /* multiple strcat */ char *mstrcat(int, nsrc, char *dst, char *src1, ...) /*... means more args to come */ { va_list srcs; /* va_list is typically void ** */ va_start(srcs, src1); /* now srcs = &src1 */ while(nsrc--){ /* repeat nsrc times */ strcat(dst, va_arg(char *, srcs)); /* va_arg(type, valist) returns *(type)valist then inclements valist */ } va_end(srcs) /* clears stack or do nothing: compiler dependent */ return dst; } You can get more details from K&R. The most important thing to remember is that in C you have to know how many number of what types of arguments are there. You C compiler will not take care of you. ---------------- ____ __ __ + Dan The "printf("%d", printf("d", printf.." Man ||__||__| + E-mail: dankg@ocf.berkeley.edu ____| ______ + Voice: +1 415-549-6111 | |__|__| + USnail: 1730 Laloma Berkeley, CA 94709 U.S.A |___ |__|__| + |____|____ + "What's the biggest U.S. export to Japan?" \_| | + "Bullshit. It makes the best fertilizer for their rice"
emm@iczer-1.UUCP (Edward M. Markowski) (06/09/90)
In article <353@ankh.ftl.fl.us> chin@ankh.ftl.fl.us (Albert Chin) writes: >How does printf() work. I believe there is some way in C to obtain the >pointer to the argument list in a function. If so, printf() is pretty >simple. But how do you do this. Is it possible to get the number of >arguments in a printf() statement? The way I was learned to pass a varible number of arguments was to make the first argument some value that could be counted. Printf gets the address of the first argument( the format string ) it scans it for % chars. When it finds a % char it uses the address of the format string to find nhe next arg. The address of the format string is a pointer to a location in the parameter stack. Printf just adds ( or subtracts ) the currect size from the pointer to the stack to get the next parameter. Printf stops going up the stack when it runs out of % chars in the format string, even if you did not pass it the correct number of args. The compiler does add any code to let the called function know how many parameters there are for it on the stack. You must make shure your function expects the same number of parameters as it was passed or pass a variadle letting it know how many parameters there are. -- Edward M. Markowski -- iczer-1 Administrator ...the garage is flooded from the sprinkler. It also left a man's decapitated body, VOICE : (201) 478-6052 lying on the floor next to his own severed head. UUCP : ..!uunet!iczer-1!emm A head which at this time has no name.
mcdaniel@amara.uucp (Tim McDaniel) (06/10/90)
In an otherwise excellent article, dankg@tornado.Berkeley.EDU (Dan KoGai) make a few minor errors: > On C convention, arguments are pushed to the stack [in] right to > left order before it jumps to the function. Not so. Arguments are often pushed on a stack left to right, or however the implementation likes. (Consider an architecture with an "argument pointer" register, pointing to the leftmost argument.) Some implementations put a few arguments in registers for efficiency: they could be the rightmost few or the leftmost few. Some calling conventions pass an argument count, but ANSI C doesn't require it. A smart compiler can see when there's no recursion and thus no need for a stack, and is permitted to put arguments in a static area or "inline" the function. In ANSI C, if a function has a variable number of arguments (like printf), it must be called with a function prototype in scope ending in ",...)", to tell the compiler this fact. The compiler may (and sometimes must) choose a different parameter-passing convention for ",..." functions than for a normal prototyped or unprototyped function. Certain schemes may be easier to implement than others, but it is a serious mistake to depend on a particular argument-passing scheme, and only the implementor has to know how it actually works. For ",..." functions, the passing method has to be such that the callee - can find the first variable argument given the last known argument (for va_start) - can get the next variable argument given the current one and the next one's expected type (for va_arg) The callee need not be told, and cannot (portably) find out - the number of actual arguments - the type (or even sizeof) of each actual argument With such loose requirements, I can think of any number of schemes. As dankg wisely notes, it is free to crash if you make a mistake in number or types. To stomp on a common misconception, by the way: section 3.3.2.2 of the ANSI C standard, page 42, lines 20-21: "The order of evaluation of the function designator, the arguments, and subexpressions within the arguments is unspecified, but there is a sequence point before the actual call." Translation: however programs actually pass arguments, you can't depend on the order of evaluation of side-effects in expressions in a function call, except that the side-effects take place before the actual call. > you have to explicity include the number of arguments somewhere in > your argument[s] and there's no other way of getting number of > arguments The two usual methods are - an argument count (e.g. in printf, derived from the contents of the format string). - a sentinel: the last argument is (int)0 or (char *)0 or some other special value. > in printf() and scanf(), et al., It's number of '%'s in format Almost: "%%" means "output a single %", and there's no argument in this case. Also, "%*" starts a scanf conversion with suppressed assignment, and there must not be an argument for it. (I know, "pick pick pick".) > And variable argument is implied by "...". Actually, by ",...". You must have a least one non-variable argument: extern foo(...); is not valid in ANSI C. > char *mstrcat(int, nsrc, char *dst, char *src1, ...) Remove first ",". > /*... means more args to come */ > { > va_list srcs; /* va_list is typically void ** */ Don't assume anything, typical or not. A va_list is a va_list, period. > va_start(srcs, src1); /* now srcs = &src1 */ > while(nsrc--){ /* repeat nsrc times */ > strcat(dst, va_arg(char *, srcs)); Arguments reversed to va_arg: va_list followed by type. > /* va_arg(type, valist) returns *(type)valist > then inclements valist */ > } > va_end(srcs) /* clears stack or do nothing: compiler dependent */ Must have a ";" at the end. > return dst; > } General problem: va_arg starts with the first variable argument. The example as given skips the last known argument, "src1". It has to be special-cased outside the loop. For this function, I would have used a sentinel rather than a count. The last argument would have to be "(char *)0" in such a scheme. > You can get more details from K&R. The most important thing to > remember is that in C you have to know how many number of what types > of arguments are there. You C compiler will not take care of you. Exactly! -- "I'm not a nerd -- I'm 'socially challenged'." Tim McDaniel Internet: mcdaniel@adi.com UUCP: {uunet,sharkey}!puffer!mcdaniel
platt@ndla.UUCP (Daniel E. Platt) (06/11/90)
In article <353@ankh.ftl.fl.us>, chin@ankh.ftl.fl.us (Albert Chin) writes: > How does printf() work. I believe there is some way in C to obtain the > pointer to the argument list in a function. If so, printf() is pretty > simple. But how do you do this. Is it possible to get the number of > arguments in a printf() statement? If you have a function 'foo', defined: void foo(a, b, c) char a, b, c; { char *pt; /* get a pointer to the stack */ pt = &a; /* ** Then: ** pt[0] <-> a; ** pt[1] <-> b; ** pt[2] <-> c; */ ... } C is set up to put the first argument on the top of the 'stack' (if there is one). Then in the case of 'printf', you can get the number of arguments by counting the number of '%'s (actually, you have to be a bit careful since '%%' is the symbol for the character '%'). Types, and therefore implementation defined sizes, are determined from what follows the % escape. It turns out that you have to be careful about sizes of things, and this isn't portable. There are macro standards set up to handle this called 'varargs.' Hope this helps, Dan
fmcwilli@oracle.oracle.com (Floyd McWilliams) (06/12/90)
In article <1990Jun8.165259.8368@agate.berkeley.edu> dankg@tornado.Berkeley.EDU (Dan KoGai) writes: > On C convention, arguments are pushed to the stack right to left >order before it jumps to the function... The order in which arguments are placed on the stack is implementation- defined. (In fact, there's nothing that requires compilers to place arguments on the stack -- they can be passed through registers.) -- Floyd McWilliams -- fmcwilli@oracle.com Protest censorship -- buy an "obscene" album next time you're in the Florida Soviet Federated Socialist Republic.
schlut@oski (Olaf Schlueter) (06/12/90)
In article <1990Jun8.165259.8368@agate.berkeley.edu> dankg@tornado.Berkeley.EDU (Dan KoGai) writes: > In article <353@ankh.ftl.fl.us> chin@ankh.ftl.fl.us (Albert Chin) writes: > >How does printf() work. I believe there is some way in C to obtain the > On C convention, arguments are pushed to the stack right to left > order before it jumps to the function. > On Ansi C, you can use <stdarg.h> macro package to write variable > argument function. And variable argument is implied by "...". Here's simple > example: [good example, which everyone shall use from now on and forever, deleted] What Dan means with 'C convention' has never been an assured standard and everyone should avoid this. It is possible to use another parameter passing scheme even in K&R (prae ANSI) C. Before ANSI C there has been no portable way to write a routine like printf in C. We should be glad that this has changed and forget about the elder days. Even with parameter passing on stack assured, how will you port a programm written on a compiler with 32 Bit int's to one with a different int size ? Mfg, (avoid E-Mail whenever possible, I have to pay for it) -- Olaf Schlueter, Sandkuhle 4-6, ! subnet: schlut@oski 2300 Kiel 1, West Germany ! USENET/DNET/EUNET: schlut@tpki.de (soon: Germany ?!) ! ZERBERUS: schlut@KBBS --- Und bitterer noch als der Tod ist das Weib (Pred 7,26) --------
scs@adam.mit.edu (Steve Summit) (06/12/90)
This topic is discussed in the frequently-asked questions list (question 25 in the June posting). Additionally, Tim McDaniel posted a number of corrections and explanations, in article <MCDANIEL.90Jun9154616@angler.amara.uucp>, to some previously- posted code. In article <314@ndla.UUCP> platt@ndla.UUCP (Daniel E. Platt) writes: >... > /* get a pointer to the stack */ > ... >C is set up to put the first argument on the top of the 'stack' (if >there is one). ...but there might not be one, and even if there is one there is no guarantee how it might be set up, which is why... >...this isn't portable. There are macro standards set up to handle this >called 'varargs.' The new, standard macro package (ANSI C) is called <stdarg.h>. Don't try to pick arguments apart "by hand;" use the standard macro package. If your compiler doesn't supply it yet, you may be able to make use of someone else's implementation of stdarg.h . This is not guaranteed to work, as the innards of a standard header may be tied to a particular implementation (particularly for something as machine-dependent as variable-length argument lists), but it is possible to write a semiportable stdarg.h that will work anywhere a "standard" macro-only (i.e. no special-cased compiler support) varargs.h would work. I posted one to alt.sources some time ago. An amended version is attached. (If stdarg.h is not an option, you could also try using the old varargs package, with which stdarg is partly compatible). Steve Summit scs@adam.mit.edu ----8<------8<----cut here for simple stdarg.h----8<------8<----- /* * stdarg implementation for "conventional" architectures with * downward-growing stacks. The stack is assumed to be aligned * to word, i.e. int, sized boundaries, with smaller arguments * widened when passed and larger arguments equal to (or rounded * up to) a multiple of the word size. * Note that this code (and indeed, any simple macro-based * approach) cannot possibly work on architectures which pass * arguments in registers. * * Another problem is that it doesn't (and can't, without help * from the compiler) know that float arguments are widened to double * when passed. Don't, therefore, call va_arg(..., float). * * Steve Summit 4/15/89 * * This code is Copyright 1989 by Steve Summit. * It may, however, be redistributed and used without restriction. */ typedef int __stacktype; typedef char __argptype; typedef __argptype *va_list; #define __argsize(thing) ((sizeof(thing) + (sizeof(__stacktype) - 1)) \ / sizeof(__stacktype) * (sizeof(__stacktype) / sizeof(__argptype))) #define va_start(vaptr, lastarg) \ vaptr = (va_list)&lastarg + __argsize(lastarg) #define va_arg(vaptr, type) \ (*(type *)(((vaptr) += __argsize(type)) - __argsize(type))) #define va_end(vaptr)
flaps@dgp.toronto.edu (Alan J Rosenthal) (06/17/90)
platt@ndla.UUCP (Daniel E. Platt) writes: >void foo(a, b, c) >char a, b, c; >{ > char *pt; > pt = &a; > /* > ** Then: > ** pt[0] <-> a; > ** pt[1] <-> b; > ** pt[2] <-> c; > */ EXTREMELY unlikely. `char' in "old-style" declarations declares an argument passed as an integer and converted to a char. If you're unlucky it won't even be on the argument stack. What you say would work for rvalue types such as int, double, long, any pointer type. ajr