这是您的代码经过大量修改的版本。我已经修改了输入代码,以便在主输入期间一次读取一行(我没有修复行计数循环;事实上,我会摆脱它,边走边算)。我使用该rewind()
功能来保存关闭和重新打开文件。我修改了代码,为了清楚起见,它使用下标表示法。我生成了 XX.YY 格式的随机数据,所以打印格式是%6.2f
为了适应它。
关键的变化在于doubleCmp()
功能。传递给它的值qsort()
是指向'array of 5 double
'的指针(或指向指针的指针double
)。这解决了大部分问题;其余的许多代码都是特殊的或古怪的,但可行。
该代码包括我的诊断打印。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int getAllDoubles(char *, double ***);
void printDouble(int, int, double **, char);
int doubleCmp(const void *, const void *);
int main(void)
{
double **arrayAll;
char *fileName = "doubles.dat";
int doublesCount = getAllDoubles(fileName, &arrayAll);
printf("%d doubles successfully copied\n", doublesCount);
if (doublesCount <= 0)
return 1;
for (int i = 0; i < doublesCount; i++)
printDouble(i, doublesCount, arrayAll, 'a');
double *tp = &arrayAll[doublesCount-1][4];
printf("The last double is at %p and is %6.2f\n", tp, *tp);
printf("The first double * is at %p\n", &arrayAll[0]);
printDouble(1, doublesCount, arrayAll, 'f');
printf("The first quaternary double is %6.2f and it's at %p\n", arrayAll[0][4], &arrayAll[0][4]);
qsort(arrayAll, doublesCount, sizeof(double *), doubleCmp);
for (int i = 0; i < doublesCount; i++)
printDouble(i, doublesCount, arrayAll, 'a');
return 0;
}
int getAllDoubles(char *filename, double ***arrayAllPtr)
{
FILE *fp;
int doublesCount = 0;
double * *arrayAll;
char c;
if ((fp = fopen(filename, "r")) == NULL)
return -1;
while ((c = getc(fp)) != EOF)
{
if (c == 'd')
doublesCount++;
}
rewind(fp);
arrayAll = *arrayAllPtr = malloc(sizeof(double *) * doublesCount);
if (arrayAll == 0)
return -1;
char line[1024];
for (int y = 0; y < doublesCount && fgets(line, sizeof(line), fp) != 0; y++)
{
arrayAll[y] = malloc(sizeof(double) * 5);
if (arrayAll[y] == 0)
return -1; // Leak
if (sscanf(line, "d %lf %lf %lf", &arrayAll[y][1], &arrayAll[y][2], &arrayAll[y][0]) != 3)
return -1; // Leak
arrayAll[y][3] = sqrt(arrayAll[y][0] + arrayAll[y][1]);
if (arrayAll[y][0] == 0)
arrayAll[y][4] = 0;
else
arrayAll[y][4] = arrayAll[y][1] / arrayAll[y][0];
}
fclose(fp);
return doublesCount;
}
void printDouble(int doubleNumber, int doublesCount, double **arrayAll, char option)
{
if (doubleNumber < 0 || doubleNumber >= doublesCount)
puts("Invalid double!");
else if (option == 'a')
{
printf("%2d: %6.2f: %6.2f: %6.2f: %6.2f: %6.2f\n", doubleNumber+1,
arrayAll[doubleNumber][0], arrayAll[doubleNumber][1],
arrayAll[doubleNumber][2], arrayAll[doubleNumber][3],
arrayAll[doubleNumber][4]);
}
else if (option == 'f')
printf("four: %6.2f\n", arrayAll[doubleNumber][4]);
}
int doubleCmp(const void *dOne, const void *dTwo)
{
double * const *d1 = dOne;
double * const *d2 = dTwo;
double const *doubleOne = *d1;
double const *doubleTwo = *d2;
printf("d1: ");
printf("[0] = %6.2f; ", doubleOne[0]);
printf("[1] = %6.2f; ", doubleOne[1]);
printf("[2] = %6.2f; ", doubleOne[2]);
printf("[3] = %6.2f; ", doubleOne[3]);
printf("[4] = %6.2f\n", doubleOne[4]);
printf("d2: ");
printf("[0] = %6.2f; ", doubleTwo[0]);
printf("[1] = %6.2f; ", doubleTwo[1]);
printf("[2] = %6.2f; ", doubleTwo[2]);
printf("[3] = %6.2f; ", doubleTwo[3]);
printf("[4] = %6.2f\n", doubleTwo[4]);
if (doubleTwo[0] < doubleOne[0])
{
/*printf("%f comes before %f\n", *doubleOne, *doubleTwo);*/
return -1;
}
else if (doubleTwo[0] > doubleOne[0])
{
/*printf("%f comes before %f\n", *doubleTwo, *doubleOne);*/
return 1;
}
else
{
/*printf("%p is equal to %p\n", *doubleOne, *doubleTwo); */
return 0;
}
}
给定这个样本数据集:
d 6.81 28.48 7.66
d 91.05 54.31 73.96
d 82.08 74.93 87.39
d 80.08 47.27 3.34
d 84.93 61.37 91.59
d 43.38 78.85 22.71
d 95.65 41.39 13.98
d 19.24 4.89 10.38
d 3.99 79.47 12.93
d 30.10 6.41 82.50
我从程序运行中得到的输出是:
10 doubles successfully copied
1: 7.66: 6.81: 28.48: 3.80: 0.89
2: 73.96: 91.05: 54.31: 12.85: 1.23
3: 87.39: 82.08: 74.93: 13.02: 0.94
4: 3.34: 80.08: 47.27: 9.13: 23.98
5: 91.59: 84.93: 61.37: 13.29: 0.93
6: 22.71: 43.38: 78.85: 8.13: 1.91
7: 13.98: 95.65: 41.39: 10.47: 6.84
8: 10.38: 19.24: 4.89: 5.44: 1.85
9: 12.93: 3.99: 79.47: 4.11: 0.31
10: 82.50: 30.10: 6.41: 10.61: 0.36
The last double is at 0x7fc86b403e50 and is 0.36
The first double * is at 0x7fc86b403a20
four: 1.23
The first quaternary double is 0.89 and it's at 0x7fc86b403a90
d1: [0] = 7.66; [1] = 6.81; [2] = 28.48; [3] = 3.80; [4] = 0.89
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d2: [0] = 82.50; [1] = 30.10; [2] = 6.41; [3] = 10.61; [4] = 0.36
d1: [0] = 73.96; [1] = 91.05; [2] = 54.31; [3] = 12.85; [4] = 1.23
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 87.39; [1] = 82.08; [2] = 74.93; [3] = 13.02; [4] = 0.94
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 3.34; [1] = 80.08; [2] = 47.27; [3] = 9.13; [4] = 23.98
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 82.50; [1] = 30.10; [2] = 6.41; [3] = 10.61; [4] = 0.36
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 91.59; [1] = 84.93; [2] = 61.37; [3] = 13.29; [4] = 0.93
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 7.66; [1] = 6.81; [2] = 28.48; [3] = 3.80; [4] = 0.89
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 12.93; [1] = 3.99; [2] = 79.47; [3] = 4.11; [4] = 0.31
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 10.38; [1] = 19.24; [2] = 4.89; [3] = 5.44; [4] = 1.85
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 13.98; [1] = 95.65; [2] = 41.39; [3] = 10.47; [4] = 6.84
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 7.66; [1] = 6.81; [2] = 28.48; [3] = 3.80; [4] = 0.89
d2: [0] = 22.71; [1] = 43.38; [2] = 78.85; [3] = 8.13; [4] = 1.91
d1: [0] = 91.59; [1] = 84.93; [2] = 61.37; [3] = 13.29; [4] = 0.93
d2: [0] = 73.96; [1] = 91.05; [2] = 54.31; [3] = 12.85; [4] = 1.23
d1: [0] = 73.96; [1] = 91.05; [2] = 54.31; [3] = 12.85; [4] = 1.23
d2: [0] = 87.39; [1] = 82.08; [2] = 74.93; [3] = 13.02; [4] = 0.94
d1: [0] = 91.59; [1] = 84.93; [2] = 61.37; [3] = 13.29; [4] = 0.93
d2: [0] = 87.39; [1] = 82.08; [2] = 74.93; [3] = 13.02; [4] = 0.94
d1: [0] = 73.96; [1] = 91.05; [2] = 54.31; [3] = 12.85; [4] = 1.23
d2: [0] = 82.50; [1] = 30.10; [2] = 6.41; [3] = 10.61; [4] = 0.36
d1: [0] = 87.39; [1] = 82.08; [2] = 74.93; [3] = 13.02; [4] = 0.94
d2: [0] = 82.50; [1] = 30.10; [2] = 6.41; [3] = 10.61; [4] = 0.36
d1: [0] = 7.66; [1] = 6.81; [2] = 28.48; [3] = 3.80; [4] = 0.89
d2: [0] = 13.98; [1] = 95.65; [2] = 41.39; [3] = 10.47; [4] = 6.84
d1: [0] = 7.66; [1] = 6.81; [2] = 28.48; [3] = 3.80; [4] = 0.89
d2: [0] = 10.38; [1] = 19.24; [2] = 4.89; [3] = 5.44; [4] = 1.85
d1: [0] = 13.98; [1] = 95.65; [2] = 41.39; [3] = 10.47; [4] = 6.84
d2: [0] = 10.38; [1] = 19.24; [2] = 4.89; [3] = 5.44; [4] = 1.85
d1: [0] = 7.66; [1] = 6.81; [2] = 28.48; [3] = 3.80; [4] = 0.89
d2: [0] = 12.93; [1] = 3.99; [2] = 79.47; [3] = 4.11; [4] = 0.31
d1: [0] = 10.38; [1] = 19.24; [2] = 4.89; [3] = 5.44; [4] = 1.85
d2: [0] = 12.93; [1] = 3.99; [2] = 79.47; [3] = 4.11; [4] = 0.31
d1: [0] = 13.98; [1] = 95.65; [2] = 41.39; [3] = 10.47; [4] = 6.84
d2: [0] = 12.93; [1] = 3.99; [2] = 79.47; [3] = 4.11; [4] = 0.31
d1: [0] = 7.66; [1] = 6.81; [2] = 28.48; [3] = 3.80; [4] = 0.89
d2: [0] = 3.34; [1] = 80.08; [2] = 47.27; [3] = 9.13; [4] = 23.98
1: 91.59: 84.93: 61.37: 13.29: 0.93
2: 87.39: 82.08: 74.93: 13.02: 0.94
3: 82.50: 30.10: 6.41: 10.61: 0.36
4: 73.96: 91.05: 54.31: 12.85: 1.23
5: 22.71: 43.38: 78.85: 8.13: 1.91
6: 13.98: 95.65: 41.39: 10.47: 6.84
7: 12.93: 3.99: 79.47: 4.11: 0.31
8: 10.38: 19.24: 4.89: 5.44: 1.85
9: 7.66: 6.81: 28.48: 3.80: 0.89
10: 3.34: 80.08: 47.27: 9.13: 23.98
您可以从诊断中看到doubleCmp()
,它能够打印出它应该比较的数据行。输出数据在第 1 列中的顺序正确(降序)。
为什么会发生变化doubleCmp()
?
在 DoubleCmp 的顶部添加更改后,它开始工作,但我不知道为什么。
前十几次你这样做,想起来很棘手。在那之后,它变得更容易并且或多或少是自动化的。
让我们考虑一个更简单的排序示例:
int array[] = { 3, 9, 12, 1, 36, -2, 0 };
enum { ARRAY_SIZE = sizeof(array) / sizeof(array[0]) };
qsort(array, ARRAY_SIZE, sizeof(array[0]), int_compare);
现在,我们知道 的签名int_compare()
必须是:
int int_compare(void const *v1, void const *v2)
{
...
}
但是那些空指针指向的是什么?答案是它们指向数组的一个元素,它是一个整数数组,所以底层类型是int *
.
int int_compare(void const *v1, void const *v2)
{
int const *ip1 = v1;
int const *ip2 = v2;
if (*ip1 < *ip2)
return -1;
else if (*ip1 > *ip2)
return +1;
else
return 0;
}
或者,由于我们处理的是简单值,我们可以使用:
int int_compare(void const *v1, void const *v2)
{
int i1 = *(int *)v1;
int i2 = *(int *)v2;
if (i1 < i2)
return -1;
else if (i1 > i2)
return +1;
else
return 0;
}
对于比较器,该组织有几个优点。首先是无论数组中的值如何,它都能正常工作。有时,您会看到有人建议使用诸如 之类的捷径return i1 - i2;
,但如果数组中的值足够大,则会遇到算术溢出问题;无论数组中的值如何,显示的代码都能正常工作。第二个是它很容易概括。如果这是比较结构数组的元素,则可以在前两个比较字段相同时添加额外的条件:
int int_compare(void const *v1, void const *v2)
{
struct dohickey const *ip1 = v1;
struct dohickey const *ip2 = v2;
if (ip1->member1 < ip2->member1)
return -1;
else if (ip1->member1 > ip2->member1)
return +1;
else if (ip1->member2 < ip2->member2)
return -1;
else if (ip1->member2 > ip2->member2)
return +1;
else
return 0;
}
冲洗并重复用于区分数组中两个值的字段。
回到您的问题,您传递给qsort()
的是一个“指向double
”的数组,其中每个指向的指针都指向一个包含 5 个值double
的数组的开头。double
当排序处理“数组int
”时,比较器接收到两个“指向int
”值的指针。当排序正在处理一个'指向double
'的指针数组时,比较器接收到两个'指向指针'的指针double
。通常,当排序处理“类型 X的数组”时,比较器会接收两个“指向X 类型的指针”值。
冒着让你困惑的风险,我还要提到你所拥有的不是“真正的二维数组”。这是使用声明的:
double array_2d[NROWS][NCOLS];
你可以通过调用来排序:
qsort(array_2d, NROWS, sizeof(array_2d[0]), array_2d_cmp);
和:
int array_2d_cmp(void const *v1, void const *v2)
{
double (*a1)[NCOLS] = (double (*)[NCOLS])v1; // Cast away const cares
double (*a2)[NCOLS] = (double (*)[NCOLS])v2; // Cast away const cares
for (int i = 0; i < NCOLS)
{
if ((*a1)[i] < (*a2)[i])
return -1;
else if ((*a1)[i] > (*a2)[i])
return +1;
}
return 0;
}
示范qsort()
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
enum { NROWS = 5, NCOLS = 7 };
static void dump_int_array_1d(char const *tag, size_t n, int a[n])
{
printf("%-8s", tag);
for (size_t i = 0; i < n; i++)
printf(" %3d", a[i]);
putchar('\n');
}
static void dump_double_array_2d(char const *tag, char const *fmt, size_t rows, size_t cols, double a[rows][cols])
{
printf("%s: (%zdx%zd)\n", tag, rows, cols);
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < cols; j++)
printf(fmt, a[i][j]);
putchar('\n');
}
}
static int array_2d_cmp(void const *v1, void const *v2)
{
double (*a1)[NCOLS] = (double (*)[NCOLS])v1;
double (*a2)[NCOLS] = (double (*)[NCOLS])v2;
//double const (* const a1)[NCOLS] = v1;
//double const (* const a2)[NCOLS] = v2;
//typedef double (*DoubleArray)[NCOLS];
//DoubleArray const a1 = v1;
//DoubleArray const a2 = v2;
for (int i = 0; i < NCOLS; i++)
{
if ((*a1)[i] < (*a2)[i])
return -1;
else if ((*a1)[i] > (*a2)[i])
return +1;
}
return 0;
}
static void sort_2d_array_double(void)
{
double array_2d[NROWS][NCOLS];
for (int row = 0; row < NROWS; row++)
{
for (int col = 0; col < NCOLS; col++)
{
int value = rand() % 10000;
array_2d[row][col] = value / 100.0;
}
}
/* Ensure there are some duplicates */
array_2d[3][0] = array_2d[1][0];
array_2d[4][0] = array_2d[0][0];
array_2d[4][1] = array_2d[0][1];
dump_double_array_2d("Before", "%6.2f", NROWS, NCOLS, array_2d);
qsort(array_2d, NROWS, sizeof(array_2d[0]), array_2d_cmp);
dump_double_array_2d("After", "%6.2f", NROWS, NCOLS, array_2d);
}
typedef int (*Comparator)(void const *v1, void const *v2);
static void sort_1d_array_int(Comparator comp_func)
{
int array[] = { 3, 9, 12, 1, 36, -2, 0 };
enum { ARRAY_SIZE = sizeof(array) / sizeof(array[0]) };
dump_int_array_1d("Before:", ARRAY_SIZE, array);
qsort(array, ARRAY_SIZE, sizeof(array[0]), comp_func);
dump_int_array_1d("After:", ARRAY_SIZE, array);
}
static int int_compare1(void const *v1, void const *v2)
{
int const *ip1 = v1;
int const *ip2 = v2;
if (*ip1 < *ip2)
return -1;
else if (*ip1 > *ip2)
return +1;
else
return 0;
}
static int int_compare2(void const *v1, void const *v2)
{
int i1 = *(int *)v1;
int i2 = *(int *)v2;
if (i1 < i2)
return -1;
else if (i1 > i2)
return +1;
else
return 0;
}
int main(void)
{
srand(time(0));
sort_2d_array_double();
sort_1d_array_int(int_compare1);
sort_1d_array_int(int_compare2);
return 0;
}