2017-03-17 2 views
1

Ich bin ein Anfänger bei C und benutze structs.Ich versuche ein Programm zu erstellen, das eine Liste von Daten sortiert. Der Benutzer gibt zuerst die Anzahl der Daten und dann die Daten selbst ein, wobei es sich um den Monat, den Tag und das Jahr handelt. Dann benutze ich qsort ich sortiere es chronologisch (zuerst nach Jahr, dann Monat, dann Tag). Ich habe versucht, das Jahr zuerst zu sortieren, aber ich bekomme nur meine Ausgabe als "0".Verwenden von qsort und Strukturen zum Sortieren einer Liste

#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

Sie haben nicht ausgefüllt, was in 'sort()' passiert, wenn das Jahr gleich ist. Der Compiler sollte eine Warnung ausgegeben haben: * Nicht alle Kontrollpfade geben einen Wert zurück. * Bis dahin, bitte '0 zurückgeben'. –

+0

Die Verwendung von 'return 0' behebt mein Problem nicht? Die Ausgabe ist immer noch 0 – LookingWest

+3

'date * ar = malloc (sizeof (int) * n);' ist nicht genug Speicher. – aschepler

Antwort

1

Es sieht so aus, als ob Sie ziemlich viel Hilfe brauchen, um alle Teile des Puzzles zusammenzusetzen. Zuerst, in Ihrem typedef von date, schließen Sie char *month ein. Das ist ein Zeiger, der nicht initialisiert wird, wenn Sie ar zuweisen, was bedeutet, dass Sie eine separate Zuordnung für ar[i].month benötigen. Sie können das tun (Sie können in diesem Fall effektiv strdup verwenden), aber warum? Wenn Sie String-Eingaben des Monats verwenden, beträgt die maximale Länge 10 Zeichen (September + nul-byte). Verwenden Sie einfach eine statisch deklarierte month oder 10 oder mehr Zeichen und vermeiden Sie die dynamische Zuordnung auf month.

Zum Beispiel können Sie hilfreiche Konstanten für die Verwendung in Ihrem Code entweder mit individuellen Anweisungen #define, oder Sie können eine globale enum, um die gleiche Sache, z.

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

Der nächste Zug-Wrack Sie in laufen mischt Zeichen und numerische Eingabe in scanf, die nicht die Eingangspuffer nicht leer (z stdin) jedes Mal, sie aufgerufen wird. Das heißt, wenn der Benutzer etwas anderes als eine gültige Dezimalzahl für 'n' eingibt (z. B. wenn er versehentlich 'q' anstelle von '1' trifft), bleibt "q\n" im Eingabepuffer, der als Eingabe für ar[0].month unten verwendet wird. Um dies zu verhindern, müssen Sie den Eingabepuffer manuell leeren (oder verwenden Sie fgets, gefolgt von sscanf, um stattdessen Benutzereingaben zu analysieren - es gibt viele Fehler bei der Verwendung von scanf für Benutzereingaben).

Trotzdem können Sie stdin ganz einfach leeren. Sie können es inline mit int c; while ((c = getchar()) != '\n' && c != EOF) {} oder eine kurze Funktion erstellen, wenn Sie es immer wieder auf die Eingabe zu reduzieren nennen, zB:

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

Wenn Sie Eingabe verwenden (ob mit der scanf Familie von Funktionen oder mit fgets (oder jedes anderes Mittel), immer Validate die Benutzereingabe. für alles, was Sie eine Katze wissen kann auf der Tastatur werden zu treten. auch immer für EOF überprüfen, welche den Benutzer abgebrochenen Eingabe von Strg + d oder Strg + z zeigt B.:

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

Vergleich mit fgets und sscanf mit der month, day, year Eingabe lesen/analysieren. Sie können etwas so einfach machen wie:

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

Es besteht keine Notwendigkeit int i als Parameter zu übergeben ist output funktioniert, erklären nur lokal, z.B.:

/* 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); 
} 

Als nächstes, während sort funktioniert, können Sie die Art von Jahr kondensieren, während potentiellen Überlauf zu vermeiden, durch Ungleichheiten statt mit:

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

Schließlich, wenn Sie Speicher zuweisen, es Es liegt in Ihrer Verantwortung, einen Zeiger auf den Start des Blocks und dann auf free den Speicher zu bewahren, wenn es nicht mehr benötigt wird. Während es auf befreit wird, gewöhnen Sie sich an, den gesamten Speicher, den Sie zuweisen, zu verfolgen und freizugeben. Gute Gewohnheiten werden Ihnen bei der Arbeit an komplexeren Projekten dienlich sein.

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

Hinweis:

es insgesamt und das Hinzufügen einer Aufforderung Einlochen zu beenden, wenn der Benutzer einfach Geben Sie schlägt stattdessen ein Datum einzugeben, könnten Sie so etwas wie die folgenden tun gibt es viele, viele Möglichkeiten so ziemlich jedem Teil des Codes zu nähern. Wenn Sie sich ansehen, wo sich ein Großteil der Zeilen befindet, werden sie in verwendet, um den Eingang zu validieren. Dies ist nur ein Minimum an Validierung. Sie würden idealerweise die Werte für jede day und gegen max/min Werte vergleichen, und Sie würden jede month gegen eine Nachschlagetabelle (oder Hash-Tabelle) vergleichen, um jeden Monat zu validieren ist ein gültiger Monat (Sie können auch Datum/Uhrzeitfunktionen verwenden, aber dass für eine andere Frage links)

Beispiel Verwendung/Output

$ ./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 

Blick über Dinge, stellen Sie sicher, dass Sie verstehen, jeden Teil von dem, was stattfindet, und fragen, ob Sie weitere Fragen haben.

Verwandte Themen