Ihr Problem vereinfacht werden würde, wenn Sie mehrere der string.h
Funktionen verwendet, wie strlen
. Außerdem müssen Sie Speicher dynamisch mit malloc
und calloc
zuweisen - ein Puffer mit fester Größe reicht hier nicht aus.
Ich präsentiere jetzt die überarbeitete reverseWords
.
char *myrev(const char *line)
{
char *revword(char *);
size_t i = strlen(line);
int inword = OUT;
size_t nWord = 0, nallocWord;
char *word; // will store the word
size_t nRet = 0, nallocRet;
char *ret; // will store the entire line, but reversed
// establish preconditions
assert(i > 0);
assert(line != NULL);
// alloc memory for word and ret
if ((word = malloc(nallocWord = INITALLOC)) != NULL &&
(ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) {
// walk backwards through line
while (i--) {
if (inword == OUT && isalnum(line[i]))
inword = IN; // we just entered a word
if (inword == IN && isalnum(line[i])) {
// we're inside a word; append current char to the word buffer
word[nWord++] = line[i];
// word buffer exhausted; reallocate
if (nWord == nallocWord)
if ((word = realloc(word, nallocWord += ALLOCSTEP)) == NULL)
return NULL;
}
// if we are in between words or at the end of the line
if (i == 0 || inword == IN && isspace(line[i])) {
inword = OUT;
word[nWord] = '\0';
word = revword(word);
// ret buffer exhausted; reallocate
if (nRet + nWord > nallocRet)
if ((ret = realloc(ret, nallocRet += ALLOCSTEP)) == NULL)
return NULL;
// append word to ret
strcat(ret, word);
strcat(ret, " ");
nRet += nWord + 1;
nWord = 0;
}
}
free(word);
// remove trailing blank
ret[strlen(ret) - 1] = '\0';
return ret;
}
// in case of mem alloc failure
return NULL;
}
Ich werde jetzt die Funktionsweise dieser Funktion erklären.
Die erste Zeile erklärt die Funktion revwords
, die ich später zeigen werde.
Die nächsten Zeilen sind die Variablendefinitionen. Die Variable i
wird als Iterator verwendet, um rückwärts zu gehen. Wir initialisieren es auf die Länge des line
-Strings, einschließlich des Null-Terminators.
Die Variable inword
ist wichtig. Es wird verwendet, um zu verfolgen, ob wir in einem Wort sind oder nicht. Es wird eine von zwei Konstanten zugewiesen: IN
und OUT
.
#define IN 0 /* inside a word */
#define OUT 1 /* outside a word */
Die nWord
und nallocWord
Variablen sind jeweils die Anzahl der Zeichen in der word
Puffer und wie viel Speicher für word
zugeordnet. word
ist, wo wir ein Wort anhäufen werden. Da die Eingabezeile rückwärts geparst wird, ist der word
Puffer zunächst rückwärts, aber wir werden ihn später umkehren.
Die Variablen nRet
und nallocRet
haben einen ähnlichen Zweck: Sie sind jeweils die Anzahl der Zeichen in der ret
Puffer und die Anzahl der für ret
zugewiesen Zeichen. ret
ist der Puffer, in dem die gesamte Eingabezeile gespeichert wird, aber die Position jedes Wortes umgekehrt ist.
Wir erzwingen dann zwei Vorbedingungen: Die Länge der Zeichenfolge muss positiv sein, und der line
Eingabepuffer darf nicht NULL sein. Wir setzen diese durch, indem wir das Makro assert
von <assert.h>
verwenden.
Wir geben jetzt das Fleisch der Funktion ein. Unsere Strategie in dieser Funktion wird sein, eine bestimmte Menge an Speicher zunächst für unsere word
und ret
Puffer zu belegen und dann später die Größe unseres Puffers bei Bedarf zu erhöhen. Also machen wir genau das.
Die Linie
if ((word = malloc(nallocWord = INITALLOC)) != NULL &&
(ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) {
erscheint zunächst erschreckend, aber wenn wir es in zwei Teile geteilt, wird es leichter sein. Der Teil links vom AND-Operator weist INITALLOC-Zeichen für word
zu und überprüft, ob der Rückgabewert nicht NULL ist (Fehler angibt). Aber INITALLOC
ist nallocWord
zugewiesen, die, wie bereits erwähnt, ist die Anzahl der Zeichen word
zugeordnet.
Der Teil rechts von der AND weist INITALLOC-Zeichen für ret
zu und überprüft, ob der Rückgabewert nicht NULL ist. Aber INITALLOC
ist nallocRet
zugewiesen. Beachten Sie, dass wir die calloc
-Funktion anstelle von malloc
verwendet haben. Der Unterschied liegt in der Tatsache, dass calloc
seinen Rückgabewert null initialisiert, aber malloc
nicht. Wir müssen unseren ret
Puffer auf Null initialisieren; Sie werden später sehen warum.
#define INITALLOC 16 /* number of characters initially alloc'ed */
#define ALLOCSTEP 32 /* number of characters to increase by */
Die Werte dieser Makros Materie nicht wirklich, aber man sollte immer noch sinnvolle Werte für sie wählen, so dass zu viele (langsam) Umschichtungen nicht durchgeführt werden.
Wie auch immer, innerhalb dieser if
Anweisung haben wir die while-Schleife, die die Zeichenfolge line
vom Ende iteriert. Die While-Schleife besteht aus einer Reihe von Tests.
Wenn wir außerhalb eines Wortes (inword == OUT
) und das aktuelle Zeichen (line[i]
) ist, alphanumerische (d.h, ein Zeichen in einem Wort), dann ändern wir inword
zu IN
.Kontrolle fällt in die nächsten if
, die
ist
Wenn wir in einem Wort sind (inword == IN
) und das aktuelle Zeichen ein Wortzeichen, dann werden wir das aktuelle Zeichen bis zum Ende der word
hinzufügen und erhöht den Charakter Zählung nWord
. Innerhalb überprüfen wir, ob word
erschöpft ist. In diesem Fall wird der Speicher neu zugewiesen. Wenn die Neuzuweisung fehlschlägt, geben Sie NULL
zurück. Die Neuzuweisung funktioniert, indem nallocWord
von ALLOCSTEP
erhöht wird, was ist, um wie viele Zeichen werden wir unseren Puffer Größe ändern.
Wenn wir zwischen den Wörtern (inword == IN && isspace(line[i]
) sind, oder wenn wir am Ende der Leitung (i == 0)
sind, dann ändern wir inword
-OUT
, Null beenden word
und Reverse es mit einem Aufruf an revword
. Unser nächster Schritt ist das Hinzufügen der word
zum Ende ret
. Wir müssen jedoch zuerst prüfen, ob genügend Platz für die Verkettung vorhanden ist. Die Bedingung nRet + nWord > nallocRet
prüft, ob die Anzahl der Zeichen in ret
plus die Anzahl der Zeichen in word
nallocRet
überschreitet, was der Anzahl der Zeichen entspricht, die für den ret
Puffer reserviert sind. Wenn die Bedingung wahr ist, wird Speicher neu zugewiesen. Wenn die Neuzuweisung fehlschlägt, geben Sie NULL
zurück. Wir brauchen die Überprüfung i == 0
, denn wenn die Schleife fertig ist, wollen wir das letzte Wort in ret
schieben.
Jetzt können wir word
-ret
mit einem Aufruf an strcat
anhängen. Wir fügen auch ein Leerzeichen hinzu, so dass die Wörter Leerzeichen zwischen einander haben.
nRet wird auf die neue Anzahl von Zeichen in ret
aktualisiert. Die + 1
ist für den Abstand zwischen Wörtern verantwortlich. nWord ist auf 0 gesetzt, daher überschreibt die nächste Schleifeniteration die alten Inhalte von word
, die nicht mehr benötigt werden.
Sobald die Schleife abgeschlossen ist, geben wir word
frei, da es länger benötigt wird, und entfernen Sie dann den nachgestellten Leerzeichen am Ende von ret
. Wir geben dann ret
zurück. Es liegt in der Verantwortung des Anrufers, diese Erinnerung freizugeben. Für jeden Anruf an malloc
/calloc
muss ein entsprechender free
vorliegen.
Kommen wir nun zu revword
, die die Funktion zum Umkehren einer Zeichenfolge ist.
char *revword(char *word)
{
char *p, *q;
assert(word != NULL);
assert(*word != '\0');
for (p = word, q = word + strlen(word) - 1; q > p; ++p, --q) {
char tmp;
tmp = *p;
*p = *q;
*q = tmp;
}
return word;
}
Die Funktion verwendet zwei Zeichenzeiger, p
und q
. ist zugeordnet, um auf den Anfang von word
zu zeigen, während q
zugeordnet ist, um auf das Ende von word
zu zeigen. Der Zeiger wird bei jeder Schleifeniteration erhöht, und q
wird dekrementiert, während q
größer als ist. Im Schleifenkörper vertauschen wir die Werte, auf die und q
zeigen.
Schließlich geben wir die umgekehrte word
zurück.
Jetzt werde ich das kleine Stück von main
zeigen, das ich änderte.
fgets(str, SIZE, stdin);
str[strlen(str) - 1] = '\0';
char *myrev(const char *line);
char *res = myrev(str);
printf("%s", res);
free(res);
Dies ist innerhalb der Schleife for (i = 0; i < N; i++)
.
Wir müssen die nachlaufende Newline aus dem str
Puffer entfernen, der dort fgets
links ist. Wir deklarieren dann die myrev
Funktion, und gespeichert als nächstes den Rückgabewert myrev
in einem temporären, so dass wir diesen Zeiger verwenden können, wenn wir free
es.
'temp' ist nicht initialisiert und zeigt nicht auf irgendwas, so dass Sie es nicht an' strcat' übergeben können. '(Wort + '')' tut nicht, was Sie denken. – aschepler
Warum verwenden Sie nicht 'strlen()', um die Länge der Eingabezeile zu erhalten? – Barmar
Ist es nicht erlaubt, Cs Standard-String-Funktionen wie 'strlen()' und 'strtok()' zu benutzen? Aber Sie dürfen 'strcat()' und 'strncpy()' verwenden? – Barmar