* better
[mascara-docs.git] / lang / C / the.ansi.c.programming.language / c.programming.notes.int / sx11b.html
blobdf58ee9f1c3b05f4aa39484fbb4dff460036600f
1 <!DOCTYPE HTML PUBLIC "-//W3O//DTD W3 HTML 2.0//EN">
2 <!-- This collection of hypertext pages is Copyright 1995-7 by Steve Summit. -->
3 <!-- This material may be freely redistributed and used -->
4 <!-- but may not be republished or sold without permission. -->
5 <html>
6 <head>
7 <link rev="owner" href="mailto:scs@eskimo.com">
8 <link rev="made" href="mailto:scs@eskimo.com">
9 <title>25.2 Writing a ``varargs'' Function</title>
10 <link href="sx11a.html" rev=precedes>
11 <link href="sx11c.html" rel=precedes>
12 <link href="sx11.html" rev=subdocument>
13 </head>
14 <body>
15 <H2>25.2 Writing a ``varargs'' Function</H2>
17 <p>In ANSI C, the head of a varargs function looks just like its prototype.
18 We will illustrate by writing our own,
19 stripped-down version of <TT>printf</TT>.
20 The bare outline of the function definition will look like this:
21 <pre>
22 void myprintf(const char *fmt, ...)
25 </pre>
26 <TT>printf</TT>'s job, of course,
29 to print its format string
30 while
31 looking for <TT>%</TT> characters and treating them specially.
32 So the main loop of <TT>printf</TT> will look like this:
33 <pre>
34 #include &lt;stdio.h&gt;
36 void myprintf(const char *fmt, ...)
38 const char *p;
40 for(p = fmt; *p != '\0'; p++)
42 if(*p != '%')
43 putchar(*p);
44 else {
45 <I>handle it specially</I>
49 </pre>
50 In this stripped-down version,
51 we won't worry about width and precision specifiers and other modifiers;
52 we'll always look at the very next character after the <TT>%</TT>
53 and assume that it's the primary format character.
54 Continuing to flesh out our outline, we get this:
55 <pre>
56 #include &lt;stdio.h&gt;
58 void myprintf(const char *fmt, ...)
60 const char *p;
62 for(p = fmt; *p != '\0'; p++)
64 if(*p != '%')
66 putchar(*p);
67 continue;
70 switch(*++p)
72 case 'c':
73 <I>fetch and print a character</I>
74 break;
76 case 'd':
77 <I>fetch and print an integer</I>
78 break;
80 case 's':
81 <I>fetch and print a string</I>
82 break;
84 case 'x':
85 <I>print an integer, in hexadecimal</I>
86 break;
88 case '%':
89 <I>print a single %</I>
90 break;
94 </pre>
95 (For clarity, we've rearranged
96 the former <TT>if</TT>/<TT>else</TT> statement slightly.
97 If the character we're looking at is not <TT>%</TT>,
98 we print it out and continue immediately
99 with the next iteration of the <TT>for</TT> loop.
100 This is a good example of the use of the <TT>continue</TT> statement.
101 Everything else in the body of the loop then
102 takes care of the case where we <em>are</em> looking at a <TT>%</TT>.)
103 </p><p>Printing these various argument types out will be relatively straightforward.
104 The $64,000 question, of course, is how to fetch the actual arguments.
105 The answer involves some specialized macros defined for us
106 by the standard header <TT>&lt;stdarg.h&gt;</TT>.
107 The macros we will use are
108 <TT>va_list</TT>,
109 <TT>va_start()</TT>,
110 <TT>va_arg()</TT>,
112 <TT>va_end()</TT>.
113 <TT>va_list</TT> is a special ``pointer'' type
114 which allows us to manipulate a variable-length argument list.
115 <TT>va_start()</TT> begins the processing of an argument list,
116 <TT>va_arg()</TT> fetches arguments from it, and
117 <TT>va_end()</TT> finishes processing.
118 (Therefore, <TT>va_list</TT> is a little bit like
119 the stdio <TT>FILE *</TT> type,
120 and <TT>va_start</TT> is a bit like <TT>fopen</TT>.)
121 </p><p>Here is the final version of our <TT>myprintf</TT> function,
122 illustrating the fetching, formatting, and printing
123 of the various argument types.
124 (For simplicity--of presentation, if nothing else--the
125 formatting step is deferred to
126 a version of
127 the nonstandard but popular <TT>itoa</TT> function.)
129 <pre>
130 #include &lt;stdio.h&gt;
131 #include &lt;stdarg.h&gt;
133 extern char *itoa(int, char *, int);
135 void myprintf(const char *fmt, ...)
137 const char *p;
138 va_list argp;
139 int i;
140 char *s;
141 char fmtbuf[256];
143 va_start(argp, fmt);
145 for(p = fmt; *p != '\0'; p++)
147 if(*p != '%')
149 putchar(*p);
150 continue;
153 switch(*++p)
155 case 'c':
156 i = va_arg(argp, int);
157 putchar(i);
158 break;
160 case 'd':
161 i = va_arg(argp, int);
162 s = itoa(i, fmtbuf, 10);
163 fputs(s, stdout);
164 break;
166 case 's':
167 s = va_arg(argp, char *);
168 fputs(s, stdout);
169 break;
171 case 'x':
172 i = va_arg(argp, int);
173 s = itoa(i, fmtbuf, 16);
174 fputs(s, stdout);
175 break;
177 case '%':
178 putchar('%');
179 break;
183 va_end(argp);
185 </pre>
186 </p><p>Looking at the new lines, we have:
187 <pre>
188 #include &lt;stdarg.h&gt;
189 </pre>
190 This header file is required in any file
191 which uses the variable argument list (<TT>va_</TT>) macros.
192 <pre>
193 va_list argp;
194 </pre>
195 This line declares a variable, <TT>argp</TT>,
196 which we use while manipulating the variable-length argument list.
197 The type of the variable is <TT>va_list</TT>,
198 a special type defined for us by <TT>&lt;stdarg.h&gt;</TT>.
199 <pre>
200 va_start(argp, fmt);
201 </pre>
202 This line initializes <TT>argp</TT>
203 and initiates the processing of the argument list.
204 The second argument to <TT>va_start()</TT> is simply
205 the name of the
206 function's
207 last fixed argument.
208 <TT>va_start()</TT> uses this
210 to figure out
211 where the variable arguments begin.
212 <pre>
213 i = va_arg(argp, int);
214 </pre>
215 And here's the heart of the matter.
216 <TT>va_arg()</TT> fetches the next argument from the argument list.
217 The second argument to <TT>va_arg()</TT> is
218 the <em>type</em> of the argument we expect.
219 Notice carefully that <em>we</em> must supply this argument,
220 which implies that <em>we</em> must somehow know
221 what type of argument to expect next.
222 The variable-length argument list machinery does <em>not</em> know.
223 In this case, we know what the type of the next argument
224 should be
225 because it's
226 supposed to match
227 the format character we're processing.
228 We can see,
229 then,
230 why such havoc results
231 when <TT>printf</TT>'s arguments
232 do not match its format string:
233 <TT>printf</TT> tells the <TT>va_arg</TT> machinery
234 to grab an argument of one type,
235 with the type determined by one of the format specifiers,
236 but since the <TT>va_arg</TT> machinery doesn't know
237 what the actual argument type is,
238 there's no way for it to do any automatic conversion.
239 If the actual argument
240 has the right type for the <TT>va_arg</TT> call which grabs it
242 of course
243 it's supposed to),
244 it works,
245 otherwise it doesn't.
246 </p><p>(You may have noticed that we fetched the character
247 to print for <TT>%c</TT>
248 as an <TT>int</TT>, not a <TT>char</TT>.
249 That's deliberate, and is explained in the next section.)
250 <pre>
251 s = va_arg(argp, char *);
252 </pre>
253 Here's another invocation of <TT>va_arg()</TT>,
254 this time fetching a string, represented as a character pointer,
255 or <TT>char *</TT>.
256 <pre>
257 va_end(argp);
258 </pre>
259 Finally, when we're all finished processing the argument list,
260 we call <TT>va_end()</TT>, which performs any necessary cleanup.
261 </p><hr>
263 Read sequentially:
264 <a href="sx11a.html" rev=precedes>prev</a>
265 <a href="sx11c.html" rel=precedes>next</a>
266 <a href="sx11.html" rev=subdocument>up</a>
267 <a href="top.html">top</a>
268 </p>
270 This page by <a href="http://www.eskimo.com/~scs/">Steve Summit</a>
271 // <a href="copyright.html">Copyright</a> 1996-1999
272 // <a href="mailto:scs@eskimo.com">mail feedback</a>
273 </p>
274 </body>
275 </html>