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.
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'. –
Die Verwendung von 'return 0' behebt mein Problem nicht? Die Ausgabe ist immer noch 0 – LookingWest
'date * ar = malloc (sizeof (int) * n);' ist nicht genug Speicher. – aschepler