📄 retaggr.920804.html
字号:
<html><!-- Mirrored from c-faq.com/malloc/retaggr.920804.html by HTTrack Website Copier/3.x [XR&CO'2008], Sat, 14 Mar 2009 08:02:59 GMT --><head><title></title></head><body>Newsgroups: comp.lang.c<br>From: scs@adam.mit.edu (Steve Summit)<br>Subject: Re: A question on style ...<br>Message-ID: <1992Aug4.170926.2335@athena.mit.edu><br>Summary: returning aggregates<br>Date: Tue, 4 Aug 1992 17:09:26 GMT<p>In article <1992Aug3.070036.20027@cucs5.cs.cuhk.hk>, wong yick pong writes:<br>> I am writing a library, of which a function should return a string:<br>> But the [routine tends to return] ... (rubbish).<br>> Is this because test is a local valuable?<br>> I know I can use static char or declare test as global,<br>> but the first method would make the code not so understand,<br>> while the second would not be possible in a library.<p>I'm not sure why using static would make the code harder tounderstand; it's one of the popular and recommended solutions.<p>I know of at least nine different ways of returning aggregates(i.e. structures and arrays, including strings) from subroutinesin C, although one of them doesn't work, two of them arecombinations, and one of them is a joke.<ol><p><li>local: the routine returns a pointer to a local array orstructure. This is the one that doesn't work.<p><li>caller-supplied: the caller passes a pointer to thereturn buffer, and its size.<p><li>malloc: the routine returns a pointer, obtained from<TT>malloc</TT>, to a dynamically-allocated region of memory; thecaller is responsible for explicitly freeing it (perhapswith a special <TT>xxxfree()</TT> routine which you also supply,if the returned structure contains other pointers whichmust also be freed).<p><li>static: the routine returns a pointer to the proverbial``static buffer whose content is overwritten by each call.''(An additional wrinkle is that several routines may sharea single static buffer, à la <TT>ctime</TT> and <TT>asctime</TT>).<p><li>a combination of 2 and 3: if the caller passes a nullpointer, the routine returns a dynamically-allocatedpointer.<p><li>a combination of 2 and 4: if the caller passes a nullpointer, the routine returns a pointer to static data.<p><li>struct: as long as the return data can be encapsulated ina single struct, the entire struct can be returned (witha conventional, by-value <TT>return</TT> statement) and thecompiler essentially worries about allocation. (Thistechnique does not work well for strings, though,especially if they are to be arbitrarily long.)<p><li>multiple static: a generalization of 4; the routinereturns a pointer to one of several static buffers.This means that the caller may use a small number ofreturn values simultaneously, and does not have to worryabout freeing them.<p><li>temporary file: the routine writes the data to atemporary file which the caller opens and reads.(Of course, if the routine picks the temporary file name,there is then the problem of retuning that name to thecaller...)</ol><p>I have heard that number 5 is a favorite of the GNU project.I like number 8 a lot, although I don't end up using it all thatoften.<p>Numbers 2 and 3 are obviously the simplest safe options,although they are more work for the caller. Between the two,number 2 is fine most of the time; number 3 is appropriate whenthe caller has no good way of estimating the required size of thereturn buffer (and the data must all be returned at once), orwhen the caller is quite likely to call malloc and store a copyof several return values anyway, and when the overhead and othermild disadvantages of dynamic memory allocation will not be aproblem.<p>Number 4, though it can be annoying and error-prone, is fine incertain circumstances, obviously those in which it is unlikelythat the caller will have to be using several return valuessimultaneously. <TT>ctime()</TT> is a good example -- its most common useis printing the current time of day, and since at any giveninstant there is only one current time of day, the caller is notlikely to need to maintain multiple separate return values.<p>Numbers 5 and 6 obviously try to have their cake and eat it too,which is sometimes appropriate, but which could lead tounnecessary complexity and confusion (i.e. unsophisticatedprogrammers might be bewildered by the discussion in a routine'sdocumentation of its multiple personalities). These techniquesshould probably be used sparingly, only when extreme flexibilitywill be truly be needed and when mostly experienced programmerswill be using the routine.<p>Number 7 is a perfectly adequate solution which is not as widelyknown and used as it could be, due to the fact that many peoplethink of structures as ``second class citizens'' in C, becauseaccording to K&R1 they were, even though Ritchie's C compiler atthe time K&R1 was published, and all quality C compilers since,and all ANSI C compilers, fully support struct assignment,passing, and return.<p>Note that if we have:<p><pre> extern struct blort blortfunc(); f() { struct blort blort1, blort2; blort1 = blortfunc(1); blort2 = blortfunc(2); ... }</pre><p>, the struct return and assignment to two local struct variableshave allowed us to manipulate two simultaneous aggregate returnvalues, without collisions (i.e. without any ``data overwrittenwith each call''), and without any manual allocation ordeallocation.<p>Number 8 is a little-known technique, which I invented myself oneday, although I've seen it mentioned once or twice on the net, soother people have obviously thought of it, too. This is anothermildly-sophisticated technique, use of which in the presence ofnovice programmers might be confusing, but which can be extremelyuseful given the right set of circumstances. I only use it forthings like string formatting routines, which produce printablestring representations of other data structures, and which arequite likely to be used more than once during a single call to,say, <TT>printf</TT>. For example, suppose I have<p><pre> char *roman(int);</pre>which returns a roman-numeral representation of an <TT>int</TT>. I mightwant to do things like<p><pre> printf("%s + %s = %s\n", roman(12), roman(34), roman(12 + 34));</pre>Now, if I were using technique 2, this would look like<p><pre> char buf1[20], buf2[20], buf3[20]; printf("%s + %s = %s\n", roman(12, buf1, 20), roman(34, buf2, 20), roman(12 + 34, buf3, 20));</pre><p>, which is a mess. And if I were using technique 3, it wouldlook like<p><pre> char *p1, *p2, *p3; printf("%s + %s = %s\n", p1 = roman(12), p2 = roman(34), p3 = roman(12 + 34)); free(p1); free(p2); free(p3);</pre><p>, which is also a mess.<p>This is obviously exactly the sort of thing that technique 4breaks down on, although of course you can do things like<p><pre> printf("%s + ", roman(12)); printf("%s = ", roman(34)); printf("%s\n", roman(12 + 34));</pre><p>But if I'm using technique 8, and if <TT>roman()</TT>'s implementationguarantees at least 3 separate static return buffers, then I canin fact write the obvious<p><pre> printf("%s + %s = %s\n", roman(12), roman(34), roman(12 + 34));</pre>and it will work just fine.<p>Technique 8's implementation looks something like<p><pre> #define NRETBUFS 3 #define RETBUFSIZE 20 char *roman(n) int n; { static char retbufs[NRETBUFS][RETBUFSIZE]; static int whichret = 0; char *ret; ret = retbufs[whichret]; whichret = (whichret + 1) % NRETBUFS; now format answer into ret... return ret; }</pre><p>I won't say anything more about technique 9.<p><address><a href="http://www.eskimo.com/~scs/">Steve Summit</a><br>scs@adam.mit.edu</address></body><!-- Mirrored from c-faq.com/malloc/retaggr.920804.html by HTTrack Website Copier/3.x [XR&CO'2008], Sat, 14 Mar 2009 08:02:59 GMT --></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -