看起來好像需要相當多的幫助將拼圖的所有部分放在一起。首先,在的date
中,您包含char *month
。這是一個指針,當您分配ar
時將會未初始化,這意味着您需要單獨分配ar[i].month
。你可以自由地這樣做(在這種情況下你可以有效地使用strdup
),但是爲什麼?如果您正在接受當月的字符串輸入,則最大長度爲10個字符(September
+ nul-byte
)。只需使用靜態聲明的month
或10
或更多字符並避免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 */
}
比較使用fgets
和sscanf
讀/解析month
,day
,year
輸入。你可以做簡單的東西如:
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 */
}
沒有必要通過int i
作爲參數的功能output
,剛剛宣佈在本地,例如:
/* 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);
}
接下來,當你的sort
功能可以正常工作,您可以凝聚的排序一年,同時避免可能的溢出,通過使用不等式來代替:
/* 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;
}
最後,如果您分配內存,它你有責任保存一個指向塊開始的指針,然後在不再需要內存的時候保存到free
。雖然它將在exit
上釋放,但要養成跟蹤和釋放所有分配內存的習慣。當你從事更復雜的項目時,良好的習慣會很好地爲你服務。
乾脆把它和添加提示退出,如果用戶只需點擊輸入,而不用輸入日期,你可以這樣做以下:
#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;
}
注:有很多,很多方面幾乎接近代碼的每個部分。如果您查看大部分行的位置,則使用它們在驗證輸入。這只是最低限度的驗證。您在理想情況下比較值爲每個day
和year
打擊最大/最小值,並且你會比較每個month
針對查找(或哈希)表來驗證每一個月是有效的月份(你也可以使用日期/時間的功能,但被留下的另外一個問題)
示例使用/輸出
$ ./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
看東西了,一定要了解什麼正在發生各個部分,並詢問你是否有進一步的問題。
年份相同時,您還未填寫'sort()'中發生的情況。編譯器應該發出一個警告:*不是所有的控制路徑都返回一個值。*在你做之前,請'返回0'。 –
使用'return 0'不能解決我的問題?輸出仍然是0 – LookingWest
'date * ar = malloc(sizeof(int)* n);'沒有足夠的內存。 – aschepler