2017-03-17 1 views
1

저는 C에서 초보자이고 structs를 사용합니다. 날짜 목록을 정렬하는 프로그램을 만들려고합니다. 사용자는 먼저 날짜 수를 입력 한 다음 날짜 자체를 월, 일 및 연도로 입력합니다. 그런 다음 qsort를 사용하여 연대순으로 정렬하고 싶습니다 (1 년에 1 년 씩 다음 달에 정렬). 나는 1 년을 먼저 정렬하려고 노력했지만 출력은 "0"으로 만 얻는다.qsort와 struct를 사용하여리스트 정렬하기

#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() '에서 어떤 일이 일어나는지 채우지 않았습니다. 컴파일러는 경고를 내야합니다 : * 모든 제어 경로가 값을 반환하지는 않습니다. * 할 때까지 'return 0'을 입력하십시오. –

+0

'return 0 '을 사용해도 내 문제가 해결되지 않습니까? 출력은 여전히 ​​0 – LookingWest

+3

'date * ar = malloc (sizeof (int) * n);'메모리가 충분하지 않습니다. – aschepler

답변

1

퍼즐의 모든 조각을 함께 모으는 데 도움이되는 것처럼 보입니다. 먼저 typedefdate 인 경우 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; 

다음 열차 사고는 입력 버퍼 (예를 들어, stdin)가 호출 될 때마다 빈하지 않는 성격과 scanf에서 숫자 입력을 혼합한다. 즉, 사용자가 'n'에 대해 올바른 십진수 이외의 값을 입력하면 (예 : 대신 실수로 'q'에 도달 한 경우) "q\n"이 입력 버퍼에 그대로 남아 있으며,이 값은 ar[0].month의 입력 값으로 간주됩니다. 이 문제를 방지하려면 수동으로 입력 버퍼를 비우거나 다음에 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) {} 
} 

당신이 (입력을 여부 (기능 또는 fgetsscanf 가족과 함께 또는 다른 방법), 항상 검증 사용자 입력. 당신이 고양이를 모두 알다시피 키보드에 스테핑 할 수있다. 또한, 항상 Ctrl 키 + D 또는 Ctrl + Z하여 사용자 취소 입력을 나타내는 EOF 확인 (바람막이) 예 :

,210
 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 */ 
    } 

는 판독/month, day, year 입력을 파싱하고 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 */ 
    } 

output을 함수에 매개 변수로 int i을 통과 할 필요가 없습니다 만, 예를 들어, 로컬로 선언 :만큼 간단한 뭔가를 할 수:

/* 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 

봐 것들 이상

사용 예/출력) 또 다른 질문에 대한 남아 있습니다.