如何qsort使用结构的指针数组?
我想按Id的顺序排列一系列指针。 但是由于我缺乏指针经验,qsort无法工作。
typedef struct block{ int Id; char * name; } block; typedef struct { block ** data; int size_array; } database; if( ( database = malloc(sizeof(database)) ) == NULL ) { printf("Error: malloc failed\n"); exit(EXIT_FAILURE); } if( ( database->data = malloc( sizeof( block * ) * database->size_array ) ) == NULL ) { exit(EXIT_FAILURE); } for( i = 0; i size_array; i++ ) { DB->data[i] = NULL; }
我正在尝试使用qsort排序,但我显然做错了什么。
int compare(const void * a, const void * b ){ const block * eval1 = a; const block * eval2 = b; if (eval1->Id Id){ return(-1); } else if (eval1->Id > eval2->Id) return 1; return 0; }
调用代码:
qsort(database->data, database->size_array, sizeof(int), compare); for(i = 0; idatabase->size_array; i++) { if(database->data[i] != NULL) printf("Id: %d\n", database->data[i]->Id); }
通过使用for循环,我看到排序失败。
插入function:
void insertE(int Id, char * name){ block * item = malloc(sizeof(block)); item->name = malloc(strlen(name)+1); strcpy(item->name, name); item->Id = Id; }
当前输出:
Id: 13 Id: 243 Id: 121 Id: 87 . . .
当您使用qsort()
(或bsearch()
或其他类似函数)时,传递给比较函数的指针属于“指向数组元素类型的指针”类型。 如果传递int
数组,则比较函数将传递给int *
(伪装为void *
)。 因此,如果你有一个block *
数组,传递给比较器的类型是一个block **
(伪装成void *
)。
因此你需要这样的东西(虽然还有其他方法可以写它):
int compare(const void *a, const void *b) { const block *eval1 = *(block **)a; const block *eval2 = *(block **)b; if (eval1->Id < eval2->Id) return(-1); else if (eval1->Id > eval2->Id) return 1; return 0; }
但是,还有其他问题。 您对qsort
呼吁是可疑的; 数组元素的大小是sizeof(db->data[0])
(假设db
是database *
类型的变量),它也是sizeof(block *)
。 一般来说,特别是在64位Unix和Windows系统上, sizeof(int) != sizeof(block *)
。 您将在sizeof(int) == sizeof(void *)
平台上使用sizeof(int)
sizeof(int) == sizeof(void *)
,这是一个包含大多数32位系统的类别。 因此,您还需要修复对qsort()
的调用:
database *db = …initialization/allocation…; qsort(db->data, db->size_array, sizeof(db->data[0]), compare);
MCVE
任何残留问题很可能与填充块指针数组的方式有关。 这是一个MCVE( 最小,完整,可validation的示例 ),显示了对块指针数组的排序。 它假定您有一个C99或C11编译器可用 – 它使用不在C90中的’复合文字’。
#include #include #include typedef struct block { int Id; char *name; } block; typedef struct { block **data; int size_array; } database; static int compare(const void *a, const void *b) { const block *eval1 = *(block **)a; const block *eval2 = *(block **)b; if (eval1->Id < eval2->Id) return(-1); else if (eval1->Id > eval2->Id) return 1; return 0; } static void dump_blocks(const char *tag, int nblocks, block * *blocks) { printf("%s:\n", tag); for (int i = 0; i < nblocks; i++) printf("Id: %3d - %s\n", blocks[i]->Id, blocks[i]->name); putchar('\n'); } int main(void) { block *b[] = { &(block){232, "RDQDLY" }, &(block){347, "XRZDMGJAZ" }, &(block){827, "QBYCVGQ" }, &(block){790, "VXSPDUX" }, &(block){245, "QRZEGGKAHD" }, &(block){717, "YGKRPIGFM" }, &(block){691, "SREIUBHVS" }, &(block){754, "ZCFLESX" }, &(block){868, "WESFFWMJ" }, &(block){291, "QCSAGIHQJ" }, }; database *db = &(database){ &b[0], sizeof(b) / sizeof(b[0]) }; dump_blocks("Before sort", db->size_array, db->data); qsort(db->data, db->size_array, sizeof(db->data[0]), compare); dump_blocks("After sort", db->size_array, db->data); return 0; }
dump_blocks()
函数遵循我觉得非常有用的模式:该函数采用首先打印的字符串标记来标识要转储的输出集,然后从数据结构中打印所有相关信息(使用其他dump_xxxxx()
如果合适的话)。 然后可以在多个地方调用它。 如果需要,我提供一个FILE *fp
输出流作为第一个参数; 这似乎没有必要。 它也可以使用fflush(fp);
或者fflush(stdout);
或者fflush(0);
在函数的末尾,以确保产生输出。 在调试崩溃的程序时,这可以提供帮助。 请注意,输出以换行符终止,以帮助确保它们及时显示。
输出示例:
Before sort: Id: 232 - RDQDLY Id: 347 - XRZDMGJAZ Id: 827 - QBYCVGQ Id: 790 - VXSPDUX Id: 245 - QRZEGGKAHD Id: 717 - YGKRPIGFM Id: 691 - SREIUBHVS Id: 754 - ZCFLESX Id: 868 - WESFFWMJ Id: 291 - QCSAGIHQJ After sort: Id: 232 - RDQDLY Id: 245 - QRZEGGKAHD Id: 291 - QCSAGIHQJ Id: 347 - XRZDMGJAZ Id: 691 - SREIUBHVS Id: 717 - YGKRPIGFM Id: 754 - ZCFLESX Id: 790 - VXSPDUX Id: 827 - QBYCVGQ Id: 868 - WESFFWMJ
你有一个block
指针数组,而不是int
数组。 sizeof
参数是数组中元素的大小,而不是要比较的数据的大小。
所以正确的呼叫将是例如
qsort(database->data, database->size_array, sizeof *database->data, compare);