2017-03-17 32 views
1

我是C初學者,使用結構體。我試圖創建一個對日期列表進行排序的程序。用戶首先輸入日期的數量,然後輸入日期本身,即月份,日期和年份。然後使用qsort我想按時間順序排序(首先是年份,然後是月份然後是一天)。我試着先排序一年,但是我只輸出了「0」。使用qsort和結構對列表進行排序

#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); 
} 
+2

年份相同時,您還未填寫'sort()'中發生的情況。編譯器應該發出一個警告:*不是所有的控制路徑都返回一個值。*在你做之前,請'返回0'。 –

+0

使用'return 0'不能解決我的問題?輸出仍然是0 – LookingWest

+3

'date * ar = malloc(sizeof(int)* n);'沒有足夠的內存。 – aschepler

回答

1

看起來好像需要相當多的幫助將拼圖的所有部分放在一起。首先,在的date中,您包含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 + dCTRL + 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讀/解析monthdayyear輸入。你可以做簡單的東西如:

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; 
} 

注:有很多,很多方面幾乎接近代碼的每個部分。如果您查看大部分行的位置,則使用它們在驗證輸入。這只是最低限度的驗證。您在理想情況下比較值爲每個dayyear打擊最大/最小值,並且你會比較每個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 

看東西了,一定要了解什麼正在發生各個部分,並詢問你是否有進一步的問題。

相關問題