gpt4 book ai didi

c - 使用qsort和struct对列表进行排序

转载 作者:太空宇宙 更新时间:2023-11-04 03:23:53 26 4
gpt4 key购买 nike

我是一个C语言的初学者,我在使用structs。我正在尝试创建一个程序来对日期列表进行排序。用户首先输入日期的数目,然后输入日期本身,即月份、日期和年份。然后使用qsort我想按时间顺序排序(先按年排序,然后按月排序,再按天排序)。我试着对今年进行了第一次分类,但我的产出只有“0”。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
char* month;
int day;
int year;
} date;

int sort(const void* a, const void* b)
{

date* date1 = (date*)a;
date* date2 = (date*)b;

if (date2->year != date1->year) {
int year2 = date2->year;
int year1 = date2->year;
if (year1 < 14) {
year1 = year1 + 100;
}
if (year2 < 14) {
year2 = year2 + 100;
}
int yearcompare = year2 - year1;
return -yearcompare;
}
}

output(date* ar, int i, int n)
{

for (i = 0; i < n; i++) {
//printf("Enter the date (month day year) i n the following format: text number number");
// printf("%s ", ar[i].month);
//printf("%d ", ar[i].day);
printf("%d\n", ar[i].year);
}
}

int main()
{
int n;
int i;
int MIN_SIZE = 0;
int MAX_SIZE = 1000;

while (1) {
printf("Enter number of dates you want to enter (between 1 and 10000):\n");
scanf("%d", &n);

if (n < MIN_SIZE) {

printf("You have entered a number lower than 0\n");
}

if (n > MAX_SIZE) {

printf("You have entered a number higher than 1000\n");
}

else {

break;
}
}

date* ar = malloc(sizeof(int) * n);
//ALLOCATE MEMORY

printf("Enter the date (month day year) in the following format: text, number(between 1 and 31), number(between 00 and 12): \n");
for (i = 0; i < n; i++) {
scanf("%s", ar[i].month);
scanf("%d", &ar[i].day);
scanf("%d", &ar[i].year);
}

qsort(ar, n, sizeof(date), sort);

output(ar, i, n);
}

最佳答案

看起来你需要很多帮助才能把所有的拼图拼凑起来。首先,在typedefdate中,包含char *month。这是一个指针,当您分配ar时,它将被取消初始化,这意味着您需要为ar[i].month单独分配一个指针。您可以自由地这样做(在这种情况下可以有效地使用strdup),但为什么?如果您正在接受月份的字符串输入,则最大长度为10个字符(September +nul-byte)。只需使用静态声明的month10或更多字符,并避免在month上进行动态分配。
例如,您可以声明有用的常量,以便在代码中使用单个#define指令,也可以使用全局enum来完成相同的任务,例如。

/* constants for max chars, max day, max year, max size */
enum { MAXC = 12, MAX_DAY = 31, MAX_YEAR = 2017, MAX_SIZE = 1000 };

typedef struct {
char month[MAXC]; /* either make static or allocate separately */
unsigned day;
unsigned year;
} date;

下一次你要遇到的列车残骸是在 scanf中混合字符和数字输入,每次调用时都不清空输入缓冲区(例如 stdin)。这意味着如果用户为 'n'输入了有效小数以外的其他值(例如,如果他意外地点击了 'q'而不是 '1'),则 "q\n"将保留在输入缓冲区中,该缓冲区将作为下面 ar[0].month的输入。为了防止这种情况发生,您需要手动清空输入缓冲区(或者使用 fgets后跟 sscanf来解析用户输入——对用户输入使用 scanf有很多陷阱)。
尽管如此,您可以很容易地清空 stdin。您可以使用 int c; while ((c = getchar()) != '\n' && c != EOF) {}内联执行,或者创建一个简短的函数,如果您反复调用它以减少键入,例如:
/* empty character remaining in stdin */
void empty_stdin ()
{
int c;
while ((c = getchar ()) != '\n' && c != EOF) {}
}

当您接受输入(无论是使用 scanf函数系列还是使用 fgets(或任何其他方式)时,请始终验证用户输入。据你所知,一只猫可能正在踩键盘。此外,始终检查 EOF这表示用户通过Ctrl+d或Ctrl+z(在windoze上)取消了输入。例如:
     while (1) {     /* obtain valid 'n', compare with using fgets below */

int rtn; /* varaible to save return of scanf -- always validate */

printf ("Enter number of dates to be entered (between 1 & 1000): ");
if ((rtn = scanf ("%d", &n)) != 1) { /* if conversion failed */
if (rtn == EOF) { /* test for user cancelation of input */
fprintf (stderr, "note: user canceled input, exiting.\n");
return 0;
} /* otherwise simply an invalid input */
fprintf (stderr, "error: invalid input.\n");
goto tryagain;
}

if (n < 0) { /* invalid input < 0 */
fprintf (stderr, "error: invalid input (n < 0).\n");
goto tryagain;
}

if (n > MAX_SIZE) { /* invalid input > MAX_SIZE */
fprintf (stderr, "error: invalid input (n > %d).\n", MAX_SIZE);
goto tryagain;
}

break; /* if we are here - we have a good value, break */

tryagain:; /* label for goto to jump over break */

empty_stdin (); /* empty characters that remain in input buffer */
}

与使用 fgetssscanf读取/解析输入进行比较。你可以做一些简单的事情:
    for (i = 0; i < n;) {   /* loop until all elements filled */

char buf[MAX_DAY + 1] = "", ans[MAXC] = "";

/* if fgets return is NULL, EOF encountered */
if (fgets (buf, MAX_DAY + 1, stdin) == NULL) {
fprintf (stderr, "note: user canceled input, exiting.\n");
return 0;
}

/* parse with sscanf, validate 3 conversion took place */
if (sscanf (buf, "%11s %u %u", ar[i].month, &ar[i].day, &ar[i].year) != 3)
{
fprintf (stderr, "error: invalid input.\n");
continue;
}

i++; /* only increment if valid sscanf conversion took place */
}

无需将 month作为参数传递给函数 day,只需在本地声明它,例如:
/* output n elements of array of struct date */
void output (date *ar, int n)
{
int i;

printf ("\nOutput sorted by year:\n\n");

for (i = 0; i < n; i++)
printf (" %s %d %d\n", ar[i].month, ar[i].day, ar[i].year);
}

下一步,虽然 year函数可以工作,但您可以使用不等式来压缩按年排序,同时避免潜在的溢出:
/* sort struct date on year */
int sort (const void *a, const void *b)
{
date *date1 = (date *) a;
date *date2 = (date *) b;

if (date2->year != date1->year)
return (date1->year > date2->year) - (date1->year < date2->year);

return 0;
}

最后,如果您分配内存,您的责任是保留指向块开头的指针,然后在不再需要内存时 int i该内存。当它在 output上释放时,养成跟踪和释放所有分配的内存的习惯。当你在做更复杂的项目时,好习惯会对你很有帮助。
如果用户只需按Enter而不是输入日期,则将其放在一起并添加退出提示,可以执行以下操作:
#include <stdio.h>
#include <stdlib.h>

/* constants for max chars, max day, max year, max size */
enum { MAXC = 12, MAX_DAY = 31, MAX_YEAR = 2017, MAX_SIZE = 1000 };

typedef struct {
char month[MAXC]; /* either make static or allocate separately */
unsigned day;
unsigned year;
} date;

/* empty character remaining in stdin */
void empty_stdin ()
{
int c;
while ((c = getchar ()) != '\n' && c != EOF) {}
}

/* sort struct date on year */
int sort (const void *a, const void *b)
{
date *date1 = (date *) a;
date *date2 = (date *) b;

if (date2->year != date1->year)
return (date1->year > date2->year) - (date1->year < date2->year);

return 0;
}

/* output n elements of array of struct date */
void output (date *ar, int n)
{
int i;

printf ("\nOutput sorted by year:\n\n");

for (i = 0; i < n; i++)
printf (" %s %d %d\n", ar[i].month, ar[i].day, ar[i].year);
}

int main (void) {

int i, n;
date *ar = NULL;

while (1) { /* obtain valid 'n', compare with using fgets below */

int rtn; /* varaible to save return of scanf -- always validate */

printf ("Enter number of dates to be entered (between 1 & 1000): ");
if ((rtn = scanf ("%d", &n)) != 1) { /* if conversion failed */
if (rtn == EOF) { /* test for user cancelation of input */
fprintf (stderr, "note: user canceled input, exiting.\n");
return 0;
} /* otherwise simply an invalid input */
fprintf (stderr, "error: invalid input.\n");
goto tryagain;
}

if (n < 0) { /* invalid input < 0 */
fprintf (stderr, "error: invalid input (n < 0).\n");
goto tryagain;
}

if (n > MAX_SIZE) { /* invalid input > MAX_SIZE */
fprintf (stderr, "error: invalid input (n > %d).\n", MAX_SIZE);
goto tryagain;
}

break; /* if we are here - we have a good value, break */

tryagain:; /* label for goto to jump over break */

empty_stdin (); /* empty characters that remain in input buffer */
}

empty_stdin (); /* empty characters that remain in input buffer */

/* allocate array of struct ar, n elements */
if ((ar = malloc (sizeof *ar * n)) == NULL) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}

/* provide format instructions */
printf ("Enter the date (month day year)\n"
" format, e.g.: Jan 18 2017\n\n");

for (i = 0; i < n;) { /* loop until all elements filled */

char buf[MAX_DAY + 1] = "", ans[MAXC] = "";

printf (" date[%2d] : ", i + 1); /* prompt for input */

/* if fgets return is NULL, EOF encountered */
if (fgets (buf, MAX_DAY + 1, stdin) == NULL) {
fprintf (stderr, "note: user canceled input, exiting.\n");
return 0;
}

if (*buf == '\n') { /* if first char is '\n', user just hit enter */
printf ("no input provided, quit (y/n)? ");
if (fgets (ans, MAXC, stdin) && (*ans == 'y' || *ans == 'Y'))
return 0;
else if (!*ans) { /* if ans NULL, EOF encountered */
fprintf (stderr, "note: user canceled input, exiting.\n");
return 0;
}
}

/* parse with sscanf, validate 3 conversion took place */
if (sscanf (buf, "%11s %u %u", ar[i].month, &ar[i].day, &ar[i].year) != 3)
{
fprintf (stderr, "error: invalid input.\n");
continue;
}

i++; /* only increment if valid sscanf conversion took place */
}

qsort (ar, n, sizeof (date), sort); /* sort by year */

output (ar, n); /* output results */

free (ar); /* free ar - you allocate it, you free it */

return 0;
}

注意:有很多很多方法可以接近代码的每个部分。如果查看大量行的位置,它们将用于验证输入。这只是验证的最低限度。理想情况下,您需要将每个 sortfree的值与max/min值进行比较,并将每个 exit与查找(或哈希)表进行比较,以验证每个月是否为有效月份(您也可以使用日期/时间函数,但这还需要回答另一个问题)
示例使用/输出
$ ./bin/qsortstruct
Enter number of dates to be entered (between 1 & 1000): 4
Enter the date (month day year)
format, e.g.: Jan 18 2017

date[ 1] : September 11 2001
date[ 2] : April 22 2010
date[ 3] : June 2 1968
date[ 4] : February 13 1979

Output sorted by year:

June 2 1968
February 13 1979
September 11 2001
April 22 2010

把事情看一遍,确保你了解正在发生的事情的每一部分,并问你是否还有其他问题。

关于c - 使用qsort和struct对列表进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42868128/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com