如何使用 C 中的整数对带有字符串的结构进行排序?
How to sort a struct with strings using its integers in C?
我正在用 C 编写一个程序,该程序应该根据奥运会金牌对国家/地区进行排名。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _Table {
char *country;
int amnt, gold, silver, bronze;
} Table;
char *arrayAlloc(int size) {
char *array;
array = (char *) malloc(sizeof(char) * size);
return array;
}
void readTable(Table *ptr) {
char buffer[100], *cpyPtr, *savePtr;
for (int i = 0; i < ptr->amnt; ++i) {
fgets(buffer, sizeof(buffer), stdin);
buffer[strlen(buffer) - 1] = '[=10=]';
ptr[i].country = strtok_r(buffer, " ", &savePtr);
ptr[i].country = strdup(ptr[i].country);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].gold = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].silver = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].bronze = strtol(cpyPtr, &cpyPtr, 10);
}
}
void printTable(Table *ptr) {
for (int i = 0; i < ptr->amnt; ++i) {
printf("%s %d %d %d\n", ptr[i].country, ptr[i].gold,ptr[i].silver, ptr[i].bronze);
}
}
int compare(const void *p, const void *q) {
int l = ((Table *)p)->gold;
int r = ((Table *)q)->gold;
return (r - l);
}
int main(int argc, char const *argv[]) {
int N; // Amount of lines
scanf("%d", &N);
getchar();
Table tab[N];
tab->amnt = N;
tab->country = arrayAlloc(100);
readTable(tab);
qsort(&tab->gold, tab->amnt, sizeof(Table), compare);
printTable(tab);
free(tab->country);
return 0;
}
输入示例:
4
BRA 3 4 5
USA 23 76 34
CHN 23 54 12
GER 10 20 23
预期输出:
USA 23 76 34
CHN 23 54 12
GER 10 20 23
BRA 3 4 5
我得到的:
BRA 23 54 12
GER 23 76 34
CHN 3 4 5
USA 10 20 23
如您所见,它似乎 "sort" 不知何故。然而,这远非我需要完成的。
我已经尝试修改 compare() 函数,但没有成功。
我可能在这里遗漏了什么?
qsort(&tab->gold, tab->amnt, sizeof(Table), compare);
前两个参数应该是指向数组的指针 (Table tab[N];
) 和数组中元素的数量 (N
),即:
qsort(tab, N, sizeof(Table), compare);
您确实在 tab->amnt
中拥有正确数量的元素,但起始指针 &tab->gold
并未指向 table 的开头,而是指向 table 的中间第一个元素,所以 qsort
无法正常工作。
另外,你有这样的循环函数:
void printTable(Table *ptr) {
for (int i = 0; i < ptr->amnt; ++i) {
这意味着您在其第一个成员中携带数组的大小。这看起来很奇怪,这意味着数组成员 'amnt' 除了数组的第一个成员外,其他所有成员都是多余的。
但是如果数组被排序,第一个数组成员不再是第一个,并且 ptr->amnt
不再包含正确的大小。
最好把printTable
改成
void printTable(Table *ptr, unsigned size) {
for (int i = 0; i < size; ++i) {
并用
调用它
printTable(tab, N);
与 readTable
相同。
我也不确定这样做的目的是什么:
tab->country = arrayAlloc(100);
您正在预分配第一个数组成员中 country
指针指向的 space,但无论如何 readTable
都会为 [=24= 分配 space ] strdup
所有数组成员。
您可能还需要考虑检查用户输入的值或 N
,或者通过 malloc
为 tab
分配内存。现在tab
分配在栈上,如果用户输入一些较大的数字导致栈溢出,可能会导致程序崩溃。
此外,由于您不检查 readTable
中 strtok_r
调用的 return 值,如果用户提供格式错误的输入,程序将崩溃。
您的代码中存在不止一个问题。
首先,如果您的 tab
动态分配会更好。
Table* tab = malloc(sizeof(Table)*N);
可变长度数组仅被 c99 及更高版本支持,如果定义了 __STDC_NO_VLA_
,某些 C11 实现可能也不支持它们(检查 C11 标准 6.10.8.3)
其次,在 tab
上迭代的所有内容都必须知道 tab
的大小,即您必须传递的 N
,例如
void readTable(Table *ptr, const int N) {
char buffer[100], *cpyPtr, *savePtr;
for (int i = 0; i < N; ++i) {...
最后qsort
需要调用如下:
qsort(tab, N,sizeof(Table), compare);
以下将产生预期的输出(请注意,我没有检查任何其他问题):
typedef struct _Table {
char *country;
int amnt, gold, silver, bronze;
} Table;
char *arrayAlloc(int size) {
char *array;
array = (char *) malloc(sizeof(char) * size);
return array;
}
void readTable(Table *ptr, const int N) {
char buffer[100], *cpyPtr, *savePtr;
for (int i = 0; i < N; ++i) {
fgets(buffer, sizeof(buffer), stdin);
buffer[strlen(buffer) - 1] = '[=13=]';
ptr[i].country = strtok_r(buffer, " ", &savePtr);
ptr[i].country = strdup(ptr[i].country);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].gold = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].silver = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].bronze = strtol(cpyPtr, &cpyPtr, 10);
}
}
void printTable(Table *ptr, const int size) {
for (int i = 0; i < size; ++i) {
printf("%s %d %d %d\n", ptr[i].country, ptr[i].gold,ptr[i].silver, ptr[i].bronze);
}
}
int compare(const void *p, const void *q) {
return ((Table *)p)->gold<((Table *)q)->gold;
}
int main(int argc, char const *argv[]) {
int N; // Amount of lines
scanf("%d", &N);
getchar();
Table* tab = malloc(sizeof(Table)*N);
readTable(tab,N);
printTable(tab,N);
qsort(tab, N,sizeof(Table), compare);
printTable(tab,N);
free(tab);
return 0;
}
输出:
BRA 3 4 5
USA 23 76 34
CHN 23 54 12
GER 10 20 23
USA 23 76 34
CHN 23 54 12
GER 10 20 23
BRA 3 4 5
有几个问题;但主要的两个如下:
在qsort(&tab->gold, tab->amnt, sizeof(Table), compare)
中,您不传递数组的开头(必要时),而是传递指向数组中第一个元素的数据成员的指针。这意味着传递的指针具有到数组开头的特定偏移量,并且几乎指向第一个元素的 "middle" 。由于 qsort
将相对于您提供给 qsort
的指针交换元素(即所提供大小的内存块),因此每个对象都可能会 "split" 并产生意想不到的奇怪结果(很可能会产生也有未定义的行为)。
其次,您使用 tab->amnt = N
,它等同于 tab[0].amnt = N
,即它只为第一个条目设置 amnt
。如果您随后对数组进行排序,则第一个条目可能会被另一个未将 amnt
设置为特定值的条目替换。所以你的 print
-函数,它再次依赖于 amnt
在第一个元素中,将算错。
所以写 qsort(tab, N, sizeof(Table), compare)
并管理元素外的数组大小,即引入一个单独的参数 size
,然后将其传递给 print
-函数。
也许还有其他问题,但这两个对我来说是最可疑的。
我正在用 C 编写一个程序,该程序应该根据奥运会金牌对国家/地区进行排名。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _Table {
char *country;
int amnt, gold, silver, bronze;
} Table;
char *arrayAlloc(int size) {
char *array;
array = (char *) malloc(sizeof(char) * size);
return array;
}
void readTable(Table *ptr) {
char buffer[100], *cpyPtr, *savePtr;
for (int i = 0; i < ptr->amnt; ++i) {
fgets(buffer, sizeof(buffer), stdin);
buffer[strlen(buffer) - 1] = '[=10=]';
ptr[i].country = strtok_r(buffer, " ", &savePtr);
ptr[i].country = strdup(ptr[i].country);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].gold = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].silver = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].bronze = strtol(cpyPtr, &cpyPtr, 10);
}
}
void printTable(Table *ptr) {
for (int i = 0; i < ptr->amnt; ++i) {
printf("%s %d %d %d\n", ptr[i].country, ptr[i].gold,ptr[i].silver, ptr[i].bronze);
}
}
int compare(const void *p, const void *q) {
int l = ((Table *)p)->gold;
int r = ((Table *)q)->gold;
return (r - l);
}
int main(int argc, char const *argv[]) {
int N; // Amount of lines
scanf("%d", &N);
getchar();
Table tab[N];
tab->amnt = N;
tab->country = arrayAlloc(100);
readTable(tab);
qsort(&tab->gold, tab->amnt, sizeof(Table), compare);
printTable(tab);
free(tab->country);
return 0;
}
输入示例:
4
BRA 3 4 5
USA 23 76 34
CHN 23 54 12
GER 10 20 23
预期输出:
USA 23 76 34
CHN 23 54 12
GER 10 20 23
BRA 3 4 5
我得到的:
BRA 23 54 12
GER 23 76 34
CHN 3 4 5
USA 10 20 23
如您所见,它似乎 "sort" 不知何故。然而,这远非我需要完成的。 我已经尝试修改 compare() 函数,但没有成功。 我可能在这里遗漏了什么?
qsort(&tab->gold, tab->amnt, sizeof(Table), compare);
前两个参数应该是指向数组的指针 (Table tab[N];
) 和数组中元素的数量 (N
),即:
qsort(tab, N, sizeof(Table), compare);
您确实在 tab->amnt
中拥有正确数量的元素,但起始指针 &tab->gold
并未指向 table 的开头,而是指向 table 的中间第一个元素,所以 qsort
无法正常工作。
另外,你有这样的循环函数:
void printTable(Table *ptr) {
for (int i = 0; i < ptr->amnt; ++i) {
这意味着您在其第一个成员中携带数组的大小。这看起来很奇怪,这意味着数组成员 'amnt' 除了数组的第一个成员外,其他所有成员都是多余的。
但是如果数组被排序,第一个数组成员不再是第一个,并且 ptr->amnt
不再包含正确的大小。
最好把printTable
改成
void printTable(Table *ptr, unsigned size) {
for (int i = 0; i < size; ++i) {
并用
调用它printTable(tab, N);
与 readTable
相同。
我也不确定这样做的目的是什么:
tab->country = arrayAlloc(100);
您正在预分配第一个数组成员中 country
指针指向的 space,但无论如何 readTable
都会为 [=24= 分配 space ] strdup
所有数组成员。
您可能还需要考虑检查用户输入的值或 N
,或者通过 malloc
为 tab
分配内存。现在tab
分配在栈上,如果用户输入一些较大的数字导致栈溢出,可能会导致程序崩溃。
此外,由于您不检查 readTable
中 strtok_r
调用的 return 值,如果用户提供格式错误的输入,程序将崩溃。
您的代码中存在不止一个问题。
首先,如果您的 tab
动态分配会更好。
Table* tab = malloc(sizeof(Table)*N);
可变长度数组仅被 c99 及更高版本支持,如果定义了 __STDC_NO_VLA_
,某些 C11 实现可能也不支持它们(检查 C11 标准 6.10.8.3)
其次,在 tab
上迭代的所有内容都必须知道 tab
的大小,即您必须传递的 N
,例如
void readTable(Table *ptr, const int N) {
char buffer[100], *cpyPtr, *savePtr;
for (int i = 0; i < N; ++i) {...
最后qsort
需要调用如下:
qsort(tab, N,sizeof(Table), compare);
以下将产生预期的输出(请注意,我没有检查任何其他问题):
typedef struct _Table {
char *country;
int amnt, gold, silver, bronze;
} Table;
char *arrayAlloc(int size) {
char *array;
array = (char *) malloc(sizeof(char) * size);
return array;
}
void readTable(Table *ptr, const int N) {
char buffer[100], *cpyPtr, *savePtr;
for (int i = 0; i < N; ++i) {
fgets(buffer, sizeof(buffer), stdin);
buffer[strlen(buffer) - 1] = '[=13=]';
ptr[i].country = strtok_r(buffer, " ", &savePtr);
ptr[i].country = strdup(ptr[i].country);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].gold = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].silver = strtol(cpyPtr, &cpyPtr, 10);
cpyPtr = strtok_r(NULL, " ", &savePtr);
ptr[i].bronze = strtol(cpyPtr, &cpyPtr, 10);
}
}
void printTable(Table *ptr, const int size) {
for (int i = 0; i < size; ++i) {
printf("%s %d %d %d\n", ptr[i].country, ptr[i].gold,ptr[i].silver, ptr[i].bronze);
}
}
int compare(const void *p, const void *q) {
return ((Table *)p)->gold<((Table *)q)->gold;
}
int main(int argc, char const *argv[]) {
int N; // Amount of lines
scanf("%d", &N);
getchar();
Table* tab = malloc(sizeof(Table)*N);
readTable(tab,N);
printTable(tab,N);
qsort(tab, N,sizeof(Table), compare);
printTable(tab,N);
free(tab);
return 0;
}
输出:
BRA 3 4 5
USA 23 76 34
CHN 23 54 12
GER 10 20 23
USA 23 76 34
CHN 23 54 12
GER 10 20 23
BRA 3 4 5
有几个问题;但主要的两个如下:
在qsort(&tab->gold, tab->amnt, sizeof(Table), compare)
中,您不传递数组的开头(必要时),而是传递指向数组中第一个元素的数据成员的指针。这意味着传递的指针具有到数组开头的特定偏移量,并且几乎指向第一个元素的 "middle" 。由于 qsort
将相对于您提供给 qsort
的指针交换元素(即所提供大小的内存块),因此每个对象都可能会 "split" 并产生意想不到的奇怪结果(很可能会产生也有未定义的行为)。
其次,您使用 tab->amnt = N
,它等同于 tab[0].amnt = N
,即它只为第一个条目设置 amnt
。如果您随后对数组进行排序,则第一个条目可能会被另一个未将 amnt
设置为特定值的条目替换。所以你的 print
-函数,它再次依赖于 amnt
在第一个元素中,将算错。
所以写 qsort(tab, N, sizeof(Table), compare)
并管理元素外的数组大小,即引入一个单独的参数 size
,然后将其传递给 print
-函数。
也许还有其他问题,但这两个对我来说是最可疑的。