快速排序 Gnu glibc qsort
2012-10-13 16:31 respawn 阅读(3298) 评论(0) 编辑 收藏 举报文笔不好,想不了太好的标题,所以就简单的将关键字列出来作为标题.
先从一个小例子开始,这个例子是使用C library中的qsort函数完成一个数组的排序:
/* qsort example */ #include <stdio.h> #include <stdlib.h> int values[] = { 40, 10, 100, 90, 20, 25 }; int compare (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); } int main () { int n; qsort (values, 6, sizeof(int), compare); for (n=0; n<6; n++) printf ("%d ",values[n]); return 0; }
代码如上,这不是我写的,是cplusplus.com上面提供的实例代码,不用质疑了. 注意一下这种使用的方法,
好吧,qsort函数参数中需要一个compare形式的函数指针,猜想一个结论:qsort函数的源码中,排序的方向
是不固定的,也就是说根据用户提供的比较函数来定,那么这个函数指针做的任务就只是确定排序方向吗?另外,
如果就是简单的确定方向问题,那么为什么不采用使用固定函数参数的形式呢?例如提供使用一个整形参数,
然后判断参数就确定排序方向. ... 这些都是我的猜想,要想知道答案,就需要看一下源码.
qsort函数式是C标准库中的函数,我们也都知道C标准以及变种,我最熟悉的就是GNU C.所以今天的qsort函数
源码来自GLIBC.另外有一天需要说明,看到很多网友在说qsort函数的时候就直接将Qsort.c的源码贴出来翻译
一下英文注解就完了.我不知道这种做法对不对,也不去评论.但是在本文中,我将会啰嗦一下,说一下是如何找到
函数源码的.(下面就是Qsort.c的源码,感兴趣的可以先看一下.)
1 /* Copyright (C) 1991,1992,1996,1997,1999,2004 Free Software Foundation, Inc. 2 This file is part of the GNU C Library. 3 Written by Douglas C. Schmidt (schmidt@ics.uci.edu). 4 5 The GNU C Library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 The GNU C Library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with the GNU C Library; if not, write to the Free 17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 02111-1307 USA. */ 19 20 /* If you consider tuning this algorithm, you should consult first: 21 Engineering a sort function; Jon Bentley and M. Douglas McIlroy; 22 Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */ 23 24 #include <alloca.h> 25 #include <limits.h> 26 #include <stdlib.h> 27 #include <string.h> 28 29 /* Byte-wise swap two items of size SIZE. */ 30 #define SWAP(a, b, size) \ 31 do \ 32 { \ 33 register size_t __size = (size); \ 34 register char *__a = (a), *__b = (b); \ 35 do \ 36 { \ 37 char __tmp = *__a; \ 38 *__a++ = *__b; \ 39 *__b++ = __tmp; \ 40 } while (--__size > 0); \ 41 } while (0) 42 43 /* Discontinue quicksort algorithm when partition gets below this size. 44 This particular magic number was chosen to work best on a Sun 4/260. */ 45 #define MAX_THRESH 4 46 47 /* Stack node declarations used to store unfulfilled partition obligations. */ 48 typedef struct 49 { 50 char *lo; 51 char *hi; 52 } stack_node; 53 54 /* The next 4 #defines implement a very fast in-line stack abstraction. */ 55 /* The stack needs log (total_elements) entries (we could even subtract 56 log(MAX_THRESH)). Since total_elements has type size_t, we get as 57 upper bound for log (total_elements): 58 bits per byte (CHAR_BIT) * sizeof(size_t). */ 59 #define STACK_SIZE (CHAR_BIT * sizeof(size_t)) 60 #define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top)) 61 #define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi))) 62 #define STACK_NOT_EMPTY (stack < top) 63 64 65 /* Order size using quicksort. This implementation incorporates 66 four optimizations discussed in Sedgewick: 67 68 1. Non-recursive, using an explicit stack of pointer that store the 69 next array partition to sort. To save time, this maximum amount 70 of space required to store an array of SIZE_MAX is allocated on the 71 stack. Assuming a 32-bit (64 bit) integer for size_t, this needs 72 only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). 73 Pretty cheap, actually. 74 75 2. Chose the pivot element using a median-of-three decision tree. 76 This reduces the probability of selecting a bad pivot value and 77 eliminates certain extraneous comparisons. 78 79 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving 80 insertion sort to order the MAX_THRESH items within each partition. 81 This is a big win, since insertion sort is faster for small, mostly 82 sorted array segments. 83 84 4. The larger of the two sub-partitions is always pushed onto the 85 stack first, with the algorithm then concentrating on the 86 smaller partition. This *guarantees* no more than log (total_elems) 87 stack size is needed (actually O(1) in this case)! */ 88 89 void 90 _quicksort (void *const pbase, size_t total_elems, size_t size, 91 __compar_d_fn_t cmp, void *arg) 92 { 93 register char *base_ptr = (char *) pbase; 94 95 const size_t max_thresh = MAX_THRESH * size; 96 97 if (total_elems == 0) 98 /* Avoid lossage with unsigned arithmetic below. */ 99 return; 100 101 if (total_elems > MAX_THRESH) 102 { 103 char *lo = base_ptr; 104 char *hi = &lo[size * (total_elems - 1)]; 105 stack_node stack[STACK_SIZE]; 106 stack_node *top = stack; 107 108 PUSH (NULL, NULL); 109 110 while (STACK_NOT_EMPTY) 111 { 112 char *left_ptr; 113 char *right_ptr; 114 115 /* Select median value from among LO, MID, and HI. Rearrange 116 LO and HI so the three values are sorted. This lowers the 117 probability of picking a pathological pivot value and 118 skips a comparison for both the LEFT_PTR and RIGHT_PTR in 119 the while loops. */ 120 121 char *mid = lo + size * ((hi - lo) / size >> 1); 122 123 if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) 124 SWAP (mid, lo, size); 125 if ((*cmp) ((void *) hi, (void *) mid, arg) < 0) 126 SWAP (mid, hi, size); 127 else 128 goto jump_over; 129 if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) 130 SWAP (mid, lo, size); 131 jump_over:; 132 133 left_ptr = lo + size; 134 right_ptr = hi - size; 135 136 /* Here's the famous ``collapse the walls'' section of quicksort. 137 Gotta like those tight inner loops! They are the main reason 138 that this algorithm runs much faster than others. */ 139 do 140 { 141 while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0) 142 left_ptr += size; 143 144 while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0) 145 right_ptr -= size; 146 147 if (left_ptr < right_ptr) 148 { 149 SWAP (left_ptr, right_ptr, size); 150 if (mid == left_ptr) 151 mid = right_ptr; 152 else if (mid == right_ptr) 153 mid = left_ptr; 154 left_ptr += size; 155 right_ptr -= size; 156 } 157 else if (left_ptr == right_ptr) 158 { 159 left_ptr += size; 160 right_ptr -= size; 161 break; 162 } 163 } 164 while (left_ptr <= right_ptr); 165 166 /* Set up pointers for next iteration. First determine whether 167 left and right partitions are below the threshold size. If so, 168 ignore one or both. Otherwise, push the larger partition's 169 bounds on the stack and continue sorting the smaller one. */ 170 171 if ((size_t) (right_ptr - lo) <= max_thresh) 172 { 173 if ((size_t) (hi - left_ptr) <= max_thresh) 174 /* Ignore both small partitions. */ 175 POP (lo, hi); 176 else 177 /* Ignore small left partition. */ 178 lo = left_ptr; 179 } 180 else if ((size_t) (hi - left_ptr) <= max_thresh) 181 /* Ignore small right partition. */ 182 hi = right_ptr; 183 else if ((right_ptr - lo) > (hi - left_ptr)) 184 { 185 /* Push larger left partition indices. */ 186 PUSH (lo, right_ptr); 187 lo = left_ptr; 188 } 189 else 190 { 191 /* Push larger right partition indices. */ 192 PUSH (left_ptr, hi); 193 hi = right_ptr; 194 } 195 } 196 } 197 198 /* Once the BASE_PTR array is partially sorted by quicksort the rest 199 is completely sorted using insertion sort, since this is efficient 200 for partitions below MAX_THRESH size. BASE_PTR points to the beginning 201 of the array to sort, and END_PTR points at the very last element in 202 the array (*not* one beyond it!). */ 203 204 #define min(x, y) ((x) < (y) ? (x) : (y)) 205 206 { 207 char *const end_ptr = &base_ptr[size * (total_elems - 1)]; 208 char *tmp_ptr = base_ptr; 209 char *thresh = min(end_ptr, base_ptr + max_thresh); 210 register char *run_ptr; 211 212 /* Find smallest element in first threshold and place it at the 213 array's beginning. This is the smallest array element, 214 and the operation speeds up insertion sort's inner loop. */ 215 216 for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size) 217 if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) 218 tmp_ptr = run_ptr; 219 220 if (tmp_ptr != base_ptr) 221 SWAP (tmp_ptr, base_ptr, size); 222 223 /* Insertion sort, running from left-hand-side up to right-hand-side. */ 224 225 run_ptr = base_ptr + size; 226 while ((run_ptr += size) <= end_ptr) 227 { 228 tmp_ptr = run_ptr - size; 229 while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) 230 tmp_ptr -= size; 231 232 tmp_ptr += size; 233 if (tmp_ptr != run_ptr) 234 { 235 char *trav; 236 237 trav = run_ptr + size; 238 while (--trav >= run_ptr) 239 { 240 char c = *trav; 241 char *hi, *lo; 242 243 for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo) 244 *hi = *lo; 245 *hi = c; 246 } 247 } 248 } 249 } 250 }
《C标准库》这本书中有讲解qsort函数,感兴趣的可以看看这本书,了解一下C标准库也不错,熟悉一下函数.
如果没有这本书,那么获取标准库函数也可以使用WIKI,途径还是蛮多的.下面就不多说了,直入正题吧.
1 /* Sort NMEMB elements of BASE, of SIZE bytes each, 2 using COMPAR to perform the comparisons. */ 3 extern void qsort (void *__base, size_t __nmemb, size_t __size, 4 __compar_fn_t __compar) __nonnull ((1, 4)); 5 #ifdef __USE_GNU 6 extern void qsort_r (void *__base, size_t __nmemb, size_t __size, 7 __compar_d_fn_t __compar, void *__arg) 8 __nonnull ((1, 4)); 9 #endif
除了提供qsort函数之外,还有一个qsort_r的函数,两者之间的名字如此相似,先不看下面一个,我们先来看一下qsort
的源码.(使用Source Insight的搜索功能很容易找得到.)
1 void 2 qsort (void *b, size_t n, size_t s, __compar_fn_t cmp) 3 { 4 return qsort_r (b, n, s, (__compar_d_fn_t) cmp, NULL); 5 }
到这里就可以看得到,qsort函数其实是调用qsort_r函数,而且依赖qsort_r函数,可以说是完全依赖.为什么这样说,
因为qsort函数在实现中仅仅是返回qsort_r函数的执行结果,没有其他任何操作,所以说是完全依赖.下面去看看qsort_r
函数.
1 void 2 qsort_r (void *b, size_t n, size_t s, __compar_d_fn_t cmp, void *arg) 3 { 4 size_t size = n * s; 5 char *tmp = NULL; 6 struct msort_param p; 7 8 /* For large object sizes use indirect sorting. */ 9 if (s > 32) 10 size = 2 * n * sizeof (void *) + s; 11 12 if (size < 1024) 13 /* The temporary array is small, so put it on the stack. */ 14 p.t = __alloca (size); 15 else 16 { 17 /* We should avoid allocating too much memory since this might 18 have to be backed up by swap space. */ 19 static long int phys_pages; 20 static int pagesize; 21 22 if (phys_pages == 0) 23 { 24 phys_pages = __sysconf (_SC_PHYS_PAGES); 25 26 if (phys_pages == -1) 27 /* Error while determining the memory size. So let's 28 assume there is enough memory. Otherwise the 29 implementer should provide a complete implementation of 30 the `sysconf' function. */ 31 phys_pages = (long int) (~0ul >> 1); 32 33 /* The following determines that we will never use more than 34 a quarter of the physical memory. */ 35 phys_pages /= 4; 36 37 pagesize = __sysconf (_SC_PAGESIZE); 38 } 39 40 /* Just a comment here. We cannot compute 41 phys_pages * pagesize 42 and compare the needed amount of memory against this value. 43 The problem is that some systems might have more physical 44 memory then can be represented with a `size_t' value (when 45 measured in bytes. */ 46 47 /* If the memory requirements are too high don't allocate memory. */ 48 if (size / pagesize > (size_t) phys_pages) 49 { 50 _quicksort (b, n, s, cmp, arg); 51 return; 52 } 53 54 /* It's somewhat large, so malloc it. */ 55 int save = errno; 56 tmp = malloc (size); 57 __set_errno (save); 58 if (tmp == NULL) 59 { 60 /* Couldn't get space, so use the slower algorithm 61 that doesn't need a temporary array. */ 62 _quicksort (b, n, s, cmp, arg); // 这里才调用Qsort.c中的 _quicksort函数. 63 return; 64 } 65 p.t = tmp; 66 } 67 68 p.s = s; 69 p.var = 4; 70 p.cmp = cmp; 71 p.arg = arg; 72 73 if (s > 32) 74 { 75 /* Indirect sorting. */ 76 char *ip = (char *) b; 77 void **tp = (void **) (p.t + n * sizeof (void *)); 78 void **t = tp; 79 void *tmp_storage = (void *) (tp + n); 80 81 while ((void *) t < tmp_storage) 82 { 83 *t++ = ip; 84 ip += s; 85 } 86 p.s = sizeof (void *); 87 p.var = 3; 88 msort_with_tmp (&p, p.t + n * sizeof (void *), n); 89 90 /* tp[0] .. tp[n - 1] is now sorted, copy around entries of 91 the original array. Knuth vol. 3 (2nd ed.) exercise 5.2-10. */ 92 char *kp; 93 size_t i; 94 for (i = 0, ip = (char *) b; i < n; i++, ip += s) 95 if ((kp = tp[i]) != ip) 96 { 97 size_t j = i; 98 char *jp = ip; 99 memcpy (tmp_storage, ip, s); 100 101 do 102 { 103 size_t k = (kp - (char *) b) / s; 104 tp[j] = jp; 105 memcpy (jp, kp, s); 106 j = k; 107 jp = kp; 108 kp = tp[k]; 109 } 110 while (kp != ip); 111 112 tp[j] = jp; 113 memcpy (jp, tmp_storage, s); 114 } 115 } 116 else 117 { 118 if ((s & (sizeof (uint32_t) - 1)) == 0 119 && ((char *) b - (char *) 0) % __alignof__ (uint32_t) == 0) 120 { 121 if (s == sizeof (uint32_t)) 122 p.var = 0; 123 else if (s == sizeof (uint64_t) 124 && ((char *) b - (char *) 0) % __alignof__ (uint64_t) == 0) 125 p.var = 1; 126 else if ((s & (sizeof (unsigned long) - 1)) == 0 127 && ((char *) b - (char *) 0) 128 % __alignof__ (unsigned long) == 0) 129 p.var = 2; 130 } 131 msort_with_tmp (&p, b, n); 132 } 133 free (tmp); 134 }
好吧,到这里我们需要找的源码以及他们之间的关系都理清楚了.在解释qsort_r源码之前,先看一下qsort函数.
Q1: __nonnull ((1, 4) 这个是什么,看到qsort函数的实现函数后,我们知道这不是一个参数,而且它是一个宏,那么到底是什么呢?
其实这还是很好想到的,即使你没有去查看它的定义.看一下这个宏里面的参数,两个数字,我在前面说到GNU
C attribute的时候有说过.这个宏是限制参数1和参数4不能为空,注意,参数顺序是从1开始的,不是我们习惯的
从0开始.都说到这里了,那我就复习一下,顺便截取一下GNU GCC手册中关于attribute nonnull的部分贴在
下面.
1 nonnull (arg-index, ...) 2 The nonnull attribute specifies that some function parameters should be non-null pointers. For instance, the declaration: 3 extern void * 4 my_memcpy (void *dest, const void *src, size_t len) 5 __attribute__((nonnull (1, 2))); 6 7 causes the compiler to check that, in calls to my_memcpy, arguments dest and src are non-null. If the compiler determines
that a null pointer is passed in an argument slot marked as non-null, and the -Wnonnull option is enabled, a warning is issued.
The compiler may also choose to make optimizations based on the knowledge that certain function arguments will not be null. 8 9 If no argument index list is given to the nonnull attribute, all pointer arguments are marked as non-null. To illustrate,
the following declaration is equivalent to the previous example: 10 11 extern void * 12 my_memcpy (void *dest, const void *src, size_t len) 13 __attribute__((nonnull));
顺便贴一下这个宏:
1 Cdefs.h (misc\sys):# define __nonnull(params) __attribute__ ((__nonnull__ params))
Q2: __compar_fn_t 这个应该是函数指针类型.根据前面的猜想,它应该是一个两个参数,而且返回整形的一个函数
指针类型.至于它是不是宏呢?不确定.查看一下这个指针可以证明猜想,下面贴出这个指针类型的源码.
1 typedef int (*__compar_fn_t) (__const void *, __const void *);
0.0. 它不是一个宏,宏大多都是以大写字母开始的,但它是一个函数指针,其中的宏__const就是const.这个都可以想的到.
好了,问题都解决了,那么GNU中的qsort是完全依赖qsort_r函数实现的,那么qsort_r中时如何使用__compar_fn_t类型指针的呢?
这需要仔细去看看qsort_r的源码,以及涉及到的Qsort.c文件中的__quicksort函数的源码.下面我们就开始吧~~(源码都给出出了)
额~ 开始不了了.. 足球比赛马上开始了~ 反正文章写的也比较长了,就到这里吧~
另外,最近在看一部关于特种兵生活的电视剧,对战友情狠感兴趣.不断的想,到底战友情是一种怎样的情感~怎样的情感~