您的排序例程应该按照编写的方式工作,但是您未能在代码中启用警告,因此您不允许编译器帮助您修复代码中的警告和错误——仅此一项就可以让您的代码运行得很好。
例如,您的编译器会告诉您确切的行号,以及多次检测到错误或警告的行中的确切字符,例如
bubblesortfn.c: In function ‘Sort’:
bubblesortfn.c:21:5: warning: return makes integer from pointer without a cast
[enabled by default]
return arr;
^
如何从指针返回一个整数?很简单,您的函数试图返回一个整数数组 (int *),并且您的函数声明为 int Sort。 (您不需要返回任何东西,但您可以通过将声明更改为 int *Sort (....) 来简单地修复它。
剩下的问题是简单的语法问题和未使用的变量(例如 main() 中的 a),它们会被您的编译器立即标记——听听。让它帮助你编写更好的代码。
始终在启用警告的情况下进行编译,并且不要接受代码,直到它在没有警告的情况下干净地编译。要启用警告,请将 -Wall -Wextra -pedantic 添加到您的 gcc/clang 编译字符串中。对于clang,您可以改用-Weverything。对于 VS(Windows 上的cl.exe),请使用/W3(或使用/Wall,但您会收到很多与代码无关的警告)。阅读并理解每个警告——然后去修复它。
正如我在评论中提到的,不要在代码中使用 幻数(除非绝对需要,例如使用 fscanf field-width 修饰符)。相反,如果您需要一个常量 #define 一个(或多个),或者使用全局 enum 来做同样的事情。这样一来,您就可以在代码的顶部有一个位置来根据需要进行更改,而无需通过声明或循环限制来进行更改。
从字面上看,修复已识别的警告并稍微整理一下就可以让您的代码正常工作并在您的 Sort 函数中正确排序数组(我还添加了一个 prnintarray() 函数来将您的数组打印到避免main(). 中的重复循环将其完全放在一起,并在使用rand() 之前调用srand() 播种随机数生成器,您可以这样做:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* if you need a constant, define one (or more) - avoid magic numbers */
#define ROWSZ 10 /* max integers to print per-row */
#define MAXB 40 /* max integers in BAT */
#define MULTP 599 /* multiplier constant */
int *Sort (int arr[], int size)
{
int i, j, a;
for (i = 0; i < size; i++) {
for (j = i + 1; j < size; j++) {
if (arr[i] > arr[j]){
a = arr[i];
arr[i] = arr[j];
arr[j] = a;
}
}
}
return arr;
}
/* simple print function to output arr of sz with rowsz int per-row */
void prnintarray (int *arr, size_t sz, size_t rowsz)
{
for (size_t i = 0; i < sz; i++) {
if (i && i % rowsz == 0)
putchar ('\n');
printf (" %4d", arr[i]);
}
putchar ('\n');
}
int main (void) {
int BAT[MAXB] = {0}; /* initialize all arrays - good practice */
srand (time(NULL)); /* seed the random number generator */
for (int i = 0; i < MAXB; i++) /* fill array */
BAT[i] = MULTP * ( (float)rand() / (float)RAND_MAX );
puts ("\nunsorted array:\n");
prnintarray (BAT, MAXB, ROWSZ);
Sort (BAT, MAXB);
puts ("\nsorted array:\n");
prnintarray (BAT, MAXB, ROWSZ);
}
使用/输出示例
$ ./bin/bubblesortfn
unsorted array:
461 519 346 508 265 93 358 407 278 151
465 531 430 148 181 227 452 206 401 202
103 518 259 267 342 495 570 431 477 455
164 339 375 511 248 42 6 8 450 284
sorted array:
6 8 42 93 103 148 151 164 181 202
206 227 248 259 265 267 278 284 339 342
346 358 375 401 407 430 431 450 452 455
461 465 477 495 508 511 518 519 531 570
查看一下,如果您还有其他问题,请告诉我。
使用qsort 进行实际排序
虽然为学习方面编写冒泡排序函数没有任何问题,但 C 库提供了 qsort,它可以而且应该满足您的大部分排序需求。使用qsort 的唯一工作是编写一个简单的compare() 函数来告诉qsort 如何对数组的相邻成员进行排序。 compare() 函数的原型通常会使新的 C 程序员陷入恐慌状态。原型是:
int compare (const void *a, const void *b)
不要让它打扰你。 a 和 b 只是指向当前正在比较的数组的两个成员的指针。您的工作是编写函数的其余部分,以便如果 a 指向的值:
- 排在
b指向的值之前,返回负数;
- 等于
b指向的值,最后返回零
- 在
b 之后排序,返回正值。 (就像strcmp)。
要处理 a 和 b 是 void 指针这一事实,您只需将它们转换为 int 指针,然后再取消引用以使用它们的值,例如
int compare (const void *a, const void *b)
{
const int *pa = a, /* a and b are pointers to elements being compared*/
*pb = b; /* in array, cast as required to proper type */
由于您的数组是int、a 和b 将是指向int 的指针,您只需将它们转换为int *。现在您可以通过指针访问这些值(例如,取消引用*pa 以获取pa 持有的地址处的值)。
现在为了满足退货要求,简单的解决方案是:
return *pa - *pb;
但是,如果*pa 是一个很大的负值而*pb 是一个很大的正值,那么减去*pa - *pb 很容易导致整数溢出 和未定义的行为时间>。通过使用两个不等式而不是直接减法,可以在提供所需返回的同时消除溢出的机会。深思熟虑:
return (*pa > *pb) - (*pa < *pb);
因此,将您的 qsort 函数放在一起并将您对 Sort 的调用替换为对 qsort 的调用,您将代码重写为:
int compare (const void *a, const void *b)
{
const int *pa = a, /* a and b are pointers to elements being compared */
*pb = b; /* in array, cast as required to proper type */
/* inequality avoids overflow from subtracting 2 large values
* (x > y) - (x < y) , returns -1, 0, 1 (like strcmp) for
* -1 -> x sorts before y, 0 -> x equals y, 1 -> x sorts after y
*/
return (*pa > *pb) - (*pa < *pb);
}
然后
qsort (BAT, MAXB, sizeof *BAT, compare);
试一试。作为大型数组的奖励,qsort 将比冒泡排序(大型数组中最慢的排序之一)快数量级