📄 faqcat1067.html
字号:
<a name="qsort1"><h1>comp.lang.c FAQ list<font color=blue>·</font><a href="../../lib/qsort1.html"><!-- qtag -->Question 13.8</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>I'm trying to sort an array of strings with <TT>qsort</TT>,using<TT>strcmp</TT>as the comparison function,but it's not working.</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>By ``array of strings'' you probably mean``array of pointers to <TT>char</TT>.''The arguments to <TT>qsort</TT>'s comparison function are pointers to theobjects being sorted, in this case, pointers to pointers to <TT>char</TT>.<TT>strcmp</TT>,however,accepts simple pointers to <TT>char</TT>.Therefore, <TT>strcmp</TT> can't be used directly.Writean intermediatecomparison function like this:<pre>/* compare strings via pointers */int pstrcmp(const void *p1, const void *p2){ return strcmp(*(char * const *)p1, *(char * const *)p2);}</pre></p><p>The comparison function'sarguments are expressed as ``generic pointers,''<TT>const void *</TT>.Theyareconvertedback to what they ``really are''(pointers to pointers to <TT>char</TT>)and dereferenced,yielding <TT>char *</TT>'s which can bepassed to <TT>strcmp</TT>.</p><p>The call to <TT>qsort</TT> might look like<pre>#include <stdlib.h>char *strings[NSTRINGS];int nstrings;/* nstrings cells of strings[] are to be sorted */qsort(strings, nstrings, sizeof(char *), pstrcmp);</pre></p><p>(Don't be misled bythe discussion in K&R2 Sec. 5.11 pp. 119-20,which is not discussingtheStandard library's<TT>qsort</TT>,andmakes a quiet,unnecessary assumption about the equivalence of <TT>char *</TT>and <TT>void *</TT>).</p><p>For more informationon<TT>qsort</TT> comparisonfunctions--howthey are called and how they must bedeclared--seequestion <a href="faqcat1067.html?sec=lib#qsort2">13.9</a>.</p><p>References:ISO Sec. 7.10.5.2<br>H&S Sec. 20.5 p. 419<hr><hr><hr><a name="qsort2"><h1>comp.lang.c FAQ list<font color=blue>·</font><a href="../../lib/qsort2.html"><!-- qtag -->Question 13.9</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>Now I'm trying to sort an array of structureswith <TT>qsort</TT>.My comparison function takes pointers to structures,but the compilercomplainsthat the function isof the wrong type for <TT>qsort</TT>.How can I cast the function pointer to shut off the warning?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>Theconversionsmust be in thecomparison function,whichmust be declared as accepting ``generic pointers''(<TT>const void *</TT>)as discussedin question<a href="faqcat1067.html?sec=lib#qsort1">13.8</a> above.For a hypotheticallittledate structure<pre>struct mystruct { int year, month, day;};</pre>thecomparison functionmight look like<a href="../../lib/compare.html" rel=subdocument>[footnote]</a><pre>int mystructcmp(const void *p1, const void *p2){ const struct mystruct *sp1 = p1; const struct mystruct *sp2 = p2; if(sp1->year < sp2->year) return -1; else if(sp1->year > sp2->year) return 1; else if(sp1->month < sp2->month) return -1; else if(sp1->month > sp2->month) return 1; else if(sp1->day < sp2->day) return -1; else if(sp1->day > sp2->day) return 1; else return 0;}</pre>(The conversionsfrom generic pointersto <TT>struct mystruct</TT> pointershappen in the initializations<TT>sp1 = p1</TT> and <TT>sp2 = p2</TT>;the compiler performs theconversions implicitlysince<TT>p1</TT> and <TT>p2</TT> are<TT>void</TT> pointers.)</p><p>For this version of <TT>mystructcmp</TT>,the call to <TT>qsort</TT> might look like<pre>#include <stdlib.h>struct mystruct dates[NDATES];int ndates;/* ndates cells of dates[] are to be sorted */qsort(dates, ndates, sizeof(struct mystruct), mystructcmp);</pre></p><p>If,on the other hand,you're sorting pointers to structures,you'll need indirection,as in question<a href="faqcat1067.html?sec=lib#qsort1">13.8</a>;thehead ofthecomparison function wouldlooklike<pre>int myptrstructcmp(const void *p1, const void *p2){ struct mystruct *sp1 = *(struct mystruct * const *)p1; struct mystruct *sp2 = *(struct mystruct * const *)p2;</pre>and the call would look like<pre>struct mystruct *dateptrs[NDATES];qsort(dateptrs, ndates, sizeof(struct mystruct *), myptrstructcmp);</pre></p><p>To understand why the curiouspointer conversionsin a <TT>qsort</TT> comparison function are necessary(and why a cast of the function pointer when calling<TT>qsort</TT>can't help),it's useful to think about how <TT>qsort</TT> works.<TT>qsort</TT> doesn't know anything about the type orrepresentation of the data being sorted:it just shuffles around little chunks of memory.(All it knows about the chunks is their size,which you specifyin <TT>qsort</TT>'s third argument.)To determine whether two chunks need swapping,<TT>qsort</TT> calls your comparison function.(To swap them,ituses the equivalent of <TT>memcpy</TT>.)</p><p>Since <TT>qsort</TT> deals in a generic waywith chunks of memory of unknowntype,it uses generic pointers(<TT>void *</TT>)to refer to them.When <TT>qsort</TT> calls your comparison function,it passes as argumentstwo generic pointers to the chunks to be compared.Since it passes generic pointers,your comparison function must<em>accept</em> generic pointers,andconvertthe pointers back to their appropriate typebefore manipulating them(i.e. before performing the comparison).A <TT>void</TT> pointer is not the same type as a structure pointer,and on some machines it may have a different size or representation(which is why these casts are required for correctness).</p><p>If you were sorting an array of structures,and had a comparison function accepting structure pointers:<pre> int mywrongstructcmp(struct mystruct *, struct mystruct *);</pre>and if you called <TT>qsort</TT> as<pre> qsort(dates, ndates, sizeof(struct mystruct), (int (*)(const void *, const void *))mywrongstructcmp); /* WRONG */</pre>thecast<TT>(int (*)(const void *, const void *))</TT>would do nothingexcept perhapssilence the message from the compiler telling you that thiscomparison functionmay<em>not</em> work with <TT>qsort</TT>.Theimplicationsof any cast you usewhen calling qsortwill have been forgottenby the time <TT>qsort</TT>gets around to calling your comparison function:it will call them with <TT>const void *</TT> arguments,so that is what your function must accept.No prototype mechanism existswhich could operate down inside <TT>qsort</TT>to convert the <TT>void</TT> pointers to <TT>struct mystruct</TT> pointers just before calling <TT>mywrongstructcmp</TT>.</p><p>In general,it is a bad idea to insert casts just to``shut the compiler up.''Compiler warnings are usually trying to tell you something,and unless you really know what you're doing,you ignore ormuzzlethem at your peril.See also question <a href="faqcatabdc.html?sec=ptrs#genericpp">4.9</a>.</p><p><a href="../../lib/sd14.html" rel=subdocument>Additional links</a></p><p>References:ISO Sec. 7.10.5.2<br>H&S Sec. 20.5 p. 419<hr><hr><hr><a name="listsort"><h1>comp.lang.c FAQ list<font color=blue>·</font><a href="../../lib/listsort.html"><!-- qtag -->Question 13.10</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>How can I sort a linked list?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>Sometimes it's easier to keep the list in order as you build it(or perhaps to use a tree instead).Algorithms like insertion sort and merge sortlend themselves ideally to use with linked lists.If you want to use a standard library function,you can allocate atemporaryarray of pointers,fill it in with pointers to all your list nodes,call <TT>qsort</TT>,and finally rebuild the list pointers based on the sorted array.</p><p>Additional links:<a href="../../lib/mergesort.ct.html">example by Chris Torek</a></p><p>References:Knuth Sec. 5.2.1 pp. 80-102, Sec. 5.2.4 pp. 159-168<br>Sedgewick Sec. 8 pp. 98-100, Sec. 12 pp. 163-175<hr><hr><hr><a name="extsort"><h1>comp.lang.c FAQ list<font color=blue>·</font><a href="../../lib/extsort.html"><!-- qtag -->Question 13.11</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>How can I sort more data than will fit in memory?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>You want an ``external sort,''whichyou can read aboutin Knuth,Volume 3.The basic idea is to sort the data in chunks(as much as will fit in memory at one time),write each sorted chunk to a temporary file,and then merge the files.Your operating system may provide a general-purpose sort utility,and if so,you can try invoking it from within your program:see questions <a href="faqcatea63.html?sec=osdep#system">19.27</a> and <a href="faqcatea63.html?sec=osdep#popen">19.30</a>,and the example in question <a href="faqcatea63.html?sec=osdep#system2">19.28</a>.</p><p>References:Knuth Sec. 5.4 pp. 247-378<br>Sedgewick Sec. 13 pp. 177-187<hr><hr><hr><a name="curtime"><h1>comp.lang.c FAQ list<font color=blue>·</font><a href="../../lib/curtime.html"><!-- qtag -->Question 13.12</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>How can I get thecurrent dateortime of day in a C program?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>Just use the <TT>time</TT>,<TT>ctime</TT>,<TT>localtime</TT>and/or <TT>strftime</TT>functions.Here is a simple example:<a href="../../lib/timefail.html" rel=subdocument>[footnote]</a><pre>#include <stdio.h>#include <time.h>int main(){ time_t now; time(&now); printf("It's %s", ctime(&now)); return 0;}</pre></p><p>Calls to <TT>localtime</TT> and <TT>strftime</TT>look like this:<pre> struct tm *tmp = localtime(&now); char fmtbuf[30]; printf("It's %d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec); strftime(fmtbuf, sizeof fmtbuf, "%A, %B %d, %Y", tmp); printf("on %s\n", fmtbuf);</pre>(Note that these functions take a <em>pointer</em>to the <TT>time_t</TT> variable,even when they will not be modifying it.<a href="../../lib/prelong.html" rel=subdocument>[footnote]</a>)</p><p>If you need sub-second resolution,see question <a href="faqcatea63.html?sec=osdep#subsecond">19.37</a>.</p><p>References:K&R2 Sec. B10 pp. 255-7<br>ISO Sec. 7.12<br>H&S Sec. 18<hr><hr><hr><a name="mktime"><h1>comp.lang.c FAQ list<font color=blue>·</font><a href="../../lib/mktime.html"><!-- qtag -->Question 13.13</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>I know that the library function <TT>localtime</TT>will convert a <TT>time_t</TT> into a broken-down <TT>struct tm</TT>,and that <TT>ctime</TT>will convert a<TT>time_t</TT> to a printable string.How can Iperform the inverse operations of convertinga <TT>struct tm</TT> or a string into a <TT>time_t</TT>?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>ANSI C specifies a library function, <TT>mktime</TT>, which converts a<TT>struct tm</TT> to a <TT>time_t</TT>.</p><p>Converting a string to a <TT>time_t</TT> is harder, because of the widevariety of date and time formats whichmight be encountered.Some systemsprovide a <TT>strptime</TT> function,which is basically the inverse of <TT>strftime</TT>.Other popular functions are<TT>partime</TT>(widely distributed with the RCS package)and<TT>getdate</TT>(and a few others,from the C news distribution).See question <a href="faqcatccbd.html?sec=resources#sources">18.16</a>.</p><p>References:K&R2 Sec. B10 p. 256<br>ISO Sec. 7.12.2.3<br>H&S Sec. 18.4 pp. 401-2<hr><hr><hr><a name="calendar"><h1>comp.lang.c FAQ list<font color=blue>·</font><a href="../../lib/calendar.html"><!-- qtag -->Question 13.14</a></h1><p><font face=Helvetica size=8 color=blue><b>Q:</b></font>How can I addNdays to a date?How can I find the difference between two dates?</p><p><hr><p><font face=Helvetica size=8 color=blue><b>A:</b></font>The ANSI/ISO Standard C <TT>mktime</TT> and<TT>difftime</TT> functionsprovidesome(limited)support forboth problems.<TT>mktime</TT> accepts non-normalized dates,so it is straightforward totake a filled-in <TT>struct tm</TT>,add or subtract from the <TT>tm_mday</TT> field,and call <TT>mktime</TT> to normalize the year, month, and day fields(andincidentallyconvert to a <TT>time_t</TT> value).<TT>difftime</TT> computes the difference,in seconds,between two <TT>time_t</TT> values;<TT>mktime</TT> can be used to compute <TT>time_t</TT> values for two dates tobe subtracted.</p><p>However,these solutionsare guaranteed to work correctlyonlyfor datesin the rangewhich can be represented as <TT>time_t</TT>'s<a href="../../lib/fn73.html" rel=subdocument>[footnote]</a>.The <TT>tm_mday</TT> field is an <TT>int</TT>,so day offsets of more than 32,736or somay cause overflow.(See below for an alternative solution without these limitations.)Note alsothatatdaylight saving timechangeovers,local daysare not24 hours long,so be carefulif you try to divideby 86,400 seconds/day.</p><p>Here is a code fragment tocompute the date 90 days past October 24, 1994:<pre>#include <stdio.h>#include <time.h>tm1.tm_mon = 10 - 1;tm1.tm_mday = 24;tm1.tm_year = 1994 - 1900;tm1.tm_hour = tm1.tm_min = tm1.tm_sec = 0;tm1.tm_isdst = -1;tm1.tm_mday += 90;if(mktime(&tm1) == -1) fprintf(stderr, "mktime failed\n");else printf("%d/%d/%d\n", tm1.tm_mon+1, tm1.tm_mday, tm1.tm_year+1900);</pre>(Setting <TT>tm_isdst</TT> to -1helps to guard against daylight saving time anomalies;setting <TT>tm_hour</TT> to 12 would, too.)</p><p>Here isa piece ofcode tocompute the differencein daysbetweenFebruary 28 and March 1 in the year 2000:<pre> struct tm tm1, tm2; time_t t1, t2; tm1.tm_mon = 2 - 1; tm1.tm_mday = 28; tm1.tm_year = 2000 - 1900; tm1.tm_hour = tm1.tm_min = tm1.tm_sec = 0; tm1.tm_isdst = -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -