为了习惯于动态创建二维数组,我想创建一个可以按每个数组中的第五个成员排序的数组。数组是一个多维数组,由一组双精度数组成。每组五个双打。最后两个双精度是根据前3个由我写的一些随机表达式计算出来的。数组的数量与它从文件中读取的双倍数组的数量一样多。当我写这篇文章的时候,我从分类中得到了非常随机的结果。
#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(){
double **arrayAll;
char *fileName = "doubles.dat";
int doublesCount = getAllDoubles(fileName, &arrayAll);
printf("%d doubles successfully copied\n", doublesCount);
printf("As a sample, here's #161\n");
printDouble(161, doublesCount, arrayAll, 'a');
double *tp = *(arrayAll + doublesCount-1) + 4;
printf("The last double is at %p and is %f\n", tp, *tp);
printf("The first double * is at %p\n", &arrayAll[0]);
printDouble(1, doublesCount, arrayAll, 'f');
printf("The first quaternary double is %f and it's at %p\n", arrayAll[0][4], &arrayAll[0][4]);
qsort(arrayAll, doublesCount, sizeof(double *), doubleCmp);
int i;
for(i = 1; i < 100; i++){
printDouble(i, doublesCount, arrayAll, 'f');
}
return 0;
}
int getAllDoubles(char *filename, double ***arrayAllPtr){
FILE *fp;
int x, y, doublesCount = 0;
double **arrayAll;
char c;
if((fp = fopen(filename, "r")) == NULL)
return -1;
while((c = getc(fp)) != EOF)
if(c == 'd')
doublesCount++;
fclose(fp);
if((fp = fopen(filename, "r")) == NULL)
return -1;
arrayAll = *arrayAllPtr = malloc(sizeof(double *) * doublesCount);
for(y = 0; (getc(fp) == 'd') && y < doublesCount; y++){
*(arrayAll+y) = malloc(sizeof(double) * 5);
if(fscanf(fp, "%lf %lf %lf", *(arrayAll + y)+1, *(arrayAll+y)+2, *(arrayAll+y)) != 3)
return -1;
*(*(arrayAll+y)+3) = sqrt(*(*(arrayAll+y)) + *(*(arrayAll+y)+1));
if(*(*(arrayAll + y)) == 0)
*(*(arrayAll+y)+4) = 0;
else
*(*(arrayAll+y)+4) = (*(*(arrayAll+y)+1) / *(*(arrayAll+y)));
while(getc(fp) != '\n')
;
}
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("one: %f\ntwo: %f\nthree: %f\nfour: %f\nfive: %f\n",
*(*(arrayAll+doubleNumber-1) + 0), *(*(arrayAll+doubleNumber-1) + 1),
*(*(arrayAll+doubleNumber-1) + 2), *(*(arrayAll+doubleNumber-1) + 3),
*(*(arrayAll+doubleNumber-1) + 4));
}
else if(option == 'f')
printf("four: %f\n", *(*(arrayAll+doubleNumber-1)+4));
return;
}
int doubleCmp(const void *dOne, const void *dTwo){
double *doubleOne, *doubleTwo;
doubleOne = (double *)dOne;
doubleTwo = (double *)dTwo;
printf("%p\n", doubleOne);
printf("initial + 1 = %p\n", doubleOne + 1);
printf("initial + 2 = %p\n", doubleOne + 2);
printf("initial + 3 = %p\n", doubleOne + 3);
printf("initial + 4 = %p\n", doubleOne + 4);
printf("%f\n\n", *doubleOne);
if((*doubleTwo) < (*doubleOne)){
/*printf("%f comes before %f\n", *doubleOne, *doubleTwo);*/
return -1;
}
else if((*doubleTwo) > (*doubleOne)){
/*printf("%f comes before %f\n", *doubleTwo, *doubleOne);*/
return 1;
}
else{
/*printf("%p is equal to %p\n", *doubleOne, *doubleTwo); */
return 0;
}
}
我输入了一些printf语句(我知道这种做法不好,但DDD在调用比较函数时不会让我继续)。我知道的是:
(在这次运行中)第一组中第一个双精度的地址(并且关联地,第一组双精度)是0x2041250。第一组中第五个double(我正在查找数组的排序依据)的地址是0x2042310。令人震惊的是,对于应该只有32个字节的地址来说,这是一个相当大的增长。
通常,这会让我相信,填充动态数组的方法可能需要修复,但我可以使用print函数很好地打印每一组double。
更奇怪的是比较函数中的printf语句。Qsort从2D数组中的第一个double*开始,它的值与第一个double的地址相同。这一切都很好,但是当我编写printf语句来查看它们在算术上做了什么时,程序产生了
(initial是指初始地址,0x2041250)
初始值+1=0x2041258//Fine
初始值+2=0x2041260//What
initial+3=0x2041268/请问?
初始值+4=0x2041270//;
再加上取消对指针的引用,这个指针作为一个空指针传入,但类型为double*,(预期)得到0.000000。
我不明白这里发生了什么。有什么想法吗?
最佳答案
这里有一个相当重的代码修改版本。我修改了输入代码,以便在主输入期间一次读取一行(行计数循环我没有修复;事实上,我会去掉它,并在运行时进行计数)。我使用rewind()
函数保存关闭和重新打开文件。为了清楚起见,我修改了代码,使其使用下标符号。我生成了格式为XX.YY的随机数据,因此打印格式是%6.2f
以适应这种情况。
关键的变化是doubleCmp()
函数。qsort()
传递给它的值是指向“array of 5double
”的指针(或指向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的顶部添加了更改之后,它开始工作了,尽管我不知道为什么。
前十几次你这样做,这是很难思考的。在那之后,它变得越来越容易和或多或少的自动化。
让我们考虑一个更简单的排序示例:
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);
现在,我们知道
doubleCmp()
的签名必须是:int int_compare(void const *v1, void const *v2)
{
...
}
但是那些空指针指向什么呢?答案是,它们指向数组的一个元素,它是一个整数数组,所以底层类型是
int_compare()
。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;
}
对于比较器来说,这个组织有两个优点。首先,不管数组中的值如何,它都能正常工作。有时,您会看到有人建议使用快捷方式,例如
int *
,但如果数组中的值足够大,则会遇到算术溢出问题;无论数组中的值如何,显示的代码都能正常工作。第二,它相当容易概括。如果这是比较结构数组的元素,则可以在前两个比较字段相同时添加额外条件: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;
}
冲洗并重复用于区分数组中两个值的字段。
回到您的问题,您传递给
return i1 - i2;
的是一个“指向qsort()
的指针”数组,其中每个指向double
的指针都指向一个由5个double
值组成的数组的开头。当排序处理“array of
double
”时,比较器接收到两个“pointer toint
”值。当排序处理“指向int
的指针数组”时,比较器接收两个“指向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;
}
double
演示#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;
}
关于c - 我的Qsort比较功能导致内存中出现奇怪的东西,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19462836/