2017-04-07 10 views
6

Das Problem mit diesem Code ist, dass es tatsächlich nichts gedruckt wird, nachdem der Benutzer in Text in der Befehlszeile eingibt.Umkehren Array in c - wird nicht gedruckt -

Der Code dient dazu, die Anzahl der Zeilen zu akzeptieren, die der Benutzer über die Eingabeaufforderung nach dem Dateinamen eingeben wird. Dann wird der Benutzer etwas eingeben, um umzukehren. Das Programm soll die Benutzereingabe für jede Zeile umkehren.

Beispiel Input = der große rote Hund

Ausgabe = Hund rot groß die

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 
#define SIZE 80 

char * reverseWords(char *string); 

//argc is the count of cmd arguments. 
//each command line argument is of type string 
int main(int argc, char *argv[]){ 

    //initialize local variables 
    int i; 
    int N; 
    char str[SIZE]; 

    for(i = 1; i <argc; i++) 
    { 
     //set N equal to the users number in the command line 
     N = atoi(argv[i]); 
    } 

    if(argc != 2){//2 means that something is in the argument. 
     printf("ERROR: Please provide an integer greater than or equal to 0"); 
     exit(1);//exit the program 
    }else if(N < 0){//We cant have a negative array size. 
     printf("ERROR: Please provide an integer greater than or equal to 0"); 
     exit(1);//exit the program 
    }else{ 
     for(i = 0; i < N; i++){ 
      /* 
      fgets(pointer to array, max # of chars copied,stdin = input from keyboard) 
      */ 
      fgets(str,SIZE,stdin); 

      printf("%s", reverseWords(str)); //<---does not print anything.... 
     }   
    } 
    return 0; 
} 


char * reverseWords(char *line){ 

    //declare local strings 
    char *temp, *word; 
    //instantiate index 
    int index = 0; 
    int word_len = 0; 
    /*set index = to size of user input 
     do this by checking if the index of line is 
     equal to the null-character. 
    */ 
    for(int i = 0; line[i] != '\0';i++) 
    { 
     index = i;//index = string length of line. 
    } 

    //check if index is less than 0. 
    //if not we decrement the index value. 

    for(index; index != -1; index--){ 
     //checking for individual words or letters 
     if(line[index] == ' ' && word_len > 0){ 
      strncpy(word,line,word_len); 
      strcat(temp , (word + ' ')); 
      word_len = 0; 

     }else if(isalnum(line[index])){ 
      word_len == word_len+1; 
     }//end if 

    }//end loop 

    //copy over the last word after the loop(if any) 
    if(word_len > 0){ 
     strncpy(word,line,word_len); 
     strcat(temp,word); 
    }//end if 
    line = temp; 
    return line; 
}//end procedure 
+5

'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

+1

Warum verwenden Sie nicht 'strlen()', um die Länge der Eingabezeile zu erhalten? – Barmar

+0

Ist es nicht erlaubt, Cs Standard-String-Funktionen wie 'strlen()' und 'strtok()' zu benutzen? Aber Sie dürfen 'strcat()' und 'strncpy()' verwenden? – Barmar

Antwort

6

Es sollte ohne Überraschung sein, dass reverseWords nichts druckt. Warum?

char * reverseWords(char *line){ 
    ... 
    char *temp, *word; 
    ... 
    line = temp; 
    return line; 
} //end procedure 

Wo steht line? (bis temp). Wo wurde temp deklariert? (in reverseWords). Wie viel Speicher wurde temp zugeordnet (keine - es ist ein nicht initialisierter Zeiger)

Weiter, was passiert mit dem Speicher verbunden mit der Funktion reverseWords, wenn es zurückkehrt? (Es ist zerstört ...), also auch wenn Sie so etwas wie char temp[strlen(line)+1] = ""; getan hätte, wäre reverseWords off wagen, in undefiniertes Verhalten weil der Zeiger Sie zurückkommen, Punkte irgendwo innerhalb des reverseWords Stapelrahmen, die zerstört wurde, als reverseWords zurückgekehrt ...

Wie behebst du das? Es gibt drei Möglichkeiten: (1) einen zweiten Zeiger auf eine zweite Array mit einer ausreichenden Lager passieren, zum Beispiel:

char *revwords (char *rline, char *line) 

oder, (2) Speicher für temp dynamisch zuzuordnen, so daß der Speicher in Verbindung mit temp die überlebt Rückkehr von reverseWords oder

(3) eine ausreichend großen Array für temp in reverseWords verwenden und line mit den Daten in temp vor der Rückkehr zu überschreiben. (Z.B. verwenden strcpy anstelle die Zuordnung line = temp;)

Während dynamische Zuordnung geradlinig ist, und in einen separaten reverseWords Arrays ist in Ordnung, werden Sie wahrscheinlich eine zweite ausreichend dimensioniert Arrays als Parameter an reverseWords besser verlaufen.

Es ist völlig unklar, was mit den argc und argv Argumente im Code tun, haben die Argumente main aus dem Beispiel unten weggelassen. Hier finden Sie ein kurzes Beispiel der Umkehrung der Wörter in jeder Zeile von stdin lesen,

#include <stdio.h> 
#include <string.h> 

#define SIZE 256 

char *revwords (char *rline, char *line); 

int main (void) { 

    char line[SIZE] = "", rline[SIZE] = ""; /* storage for line/rline */ 

    while (fgets (line, SIZE, stdin)) { /* for each line on stdin */ 
     printf ("\n line: %s\nrline: %s\n", line, revwords (rline, line)); 
     *rline = 0; /* set first char in rline to nul-byte */ 
    } 

    return 0; 
} 

char *revwords (char *rline, char *line) 
{ 
    size_t lnlen = strlen (line); /* length of line */ 
    /* pointer, end-pointer, rev-pointer and flag pointer-to-space */ 
    char *p = line + lnlen - 1, *ep = p, *rp = rline, *p2space = NULL; 

    if (!line || !*line) { /* validate line not NULL and not empty */ 
     fprintf (stderr, "revwords() error: 'line' empty of null.\n"); 
     return NULL; 
    } 

    if (*ep == '\n') /* if line ends in '\n' -- remove it */ 
     *ep-- = 0; 
    else    /* warn if no '\n' present in line */ 
     fprintf (stderr, "warning: no POSIX '\\n' found in line.\n"); 

    for (; ep >= line; ep--) { /* for each char from end-to-beginning */ 
     if (*ep == ' ') {    /* is it a space? */ 
      size_t len = p - ep;  /* get the length of the word */ 
      strncat (rp, ep + 1, len); /* concatenate word to rline */ 
      if (p == line + lnlen - 1) /* if first word, append ' ' */ 
       strcat (rp, " "); 
      p = ep;      /* update p to last ' ' */ 
      p2space = ep;    /* set flag to valid pointer */ 
     } 
    } 
    strncat (rp, line, p - line);  /* handle first/last word */ 

    if (!p2space) { /* validate line contained ' ', if not return NULL */ 
     fprintf (stderr, "revwords() error: nothing to reverse.\n"); 
     return NULL; 
    } 

    return rline; /* return pointer to reversed line */ 
} 

Hinweis: wenn es keine '\n' in line wenn zu revwords bestanden, versuchen Sie wahrscheinlich eine Zeile mehr lesen als SIZE Zeichen (oder Sie lesen die letzte Zeile, wo es keine POSIX '\n' am Ende der Datei ist), und Sie müssen das wie erforderlich behandeln. Hier warne ich einfach.

Beispiel Verwendung/Output

$ printf "my dog has fleas\nmy cat does too\n" | ./bin/str_rev_words 

line: my dog has fleas 
rline: fleas has dog my 

line: my cat does too 
rline: too does cat my 

Blick über Dinge und lassen Sie mich wissen, wenn Sie Fragen haben. Es gibt Dutzende Möglichkeiten, dieses Problem anzugehen, niemandem mehr Recht als dem anderen, wenn sie die Umkehrung in einer vernünftig effizienten Weise richtig handhaben. Treffen Sie Ihre Wahl.

Wenn Sie wie die String-Library-Funktionen anstelle von Pointer-Arithmetik verwenden, könnte man immer etwas tun, wie folgt aus:

char *revwords (char *rline, char *line) 
{ 
    /* length, pointer, end-pointer, pointer-to-space, copy of line */ 
    size_t len = strlen (line); 
    char *p = NULL, *p2space = NULL, copy[len+1]; 

    if (!line || !*line) { /* validate line not NULL and not empty */ 
     fprintf (stderr, "revwords() error: 'line' empty of null.\n"); 
     return NULL; 
    } 

    if (line[len-1] == '\n') /* remove trailing newline */ 
     line[--len] = 0; 
    else    /* warn if no '\n' present in line */ 
     fprintf (stderr, "warning: no POSIX '\\n' found in line.\n"); 

    strncpy (copy, line, len + 1); /* copy line to 'copy' */ 

    /* for each ' ' from end-to-beginning */ 
    while ((p = strrchr (copy, ' '))) { 
     strcat (rline, p + 1);   /* append word to rline */ 
     strcat (rline, " ");   /* followed by a space */ 
     p2space = p;     /* set p2space to p  */ 
     *p2space = 0;     /* nul-terminate copy at p */ 
    } 

    if (p2space) {    /* validate space found in line */ 
     *p2space = 0;   /* nul-terminate at space  */ 
     strcat (rline, copy); /* concatenate first/last word */ 
    } 
    else {      /* no ' ' in line, return NULL */ 
     fprintf (stderr, "revwords() error: nothing to reverse.\n"); 
     return NULL; 
    } 

    return rline; /* return pointer to reversed line */ 
} 

Hinweis: Während kein Fehler, vermeidet der Standard-Codierung Stil für C die Verwendung von caMelCase oder MixedCase Variablen- oder Funktionsnamen für alle Kleinbuchstaben bei der Reservierung Großbuchstaben Namen für die Verwendung mit Makros und Konstanten. Belassen Sie caMelCase oder MixedCase für Java oder C++. (Es ist Stil, so ist es Ihre Wahl, aber es sagt etwas über Ihren Code auf den ersten Eindruck)

+0

... es sei denn, Ihr C-Code verwendet Xlib, wo Mischfall unvermeidbar ist – technosaurus

+0

Zugegeben, aber wenn Sie Xlib geschrieben haben - es ist nicht in Ihrer Kontrolle an diesem Punkt. –

1

es Ihnen wie Schwierigkeiten zu sein scheint.

Nehmen Sie dies für Ihre Zwecke

#include <string.h> 
#include <stdio.h> 

char* reverse_words(char* str); 

int main() { 
    char arr[] = "the big red dog"; 
    printf("%s", reverse_words(arr)); 
    return 0; 
} 


char* reverse_words(char* str) { 
    char delim = ' '; // space 
    int left = 0; 
    int reverse_index = 0; 
    int right = 0; 
    int len = strlen(str); 
    char tmp; 
    while (left < len) { 
     while (str[right] != delim && right < len) 
      right++;  
     reverse_index = right - 1; 
     while (left < reverse_index){ 
      tmp = str[left]; 
      str[left] = str[reverse_index]; 
      str[reverse_index] = tmp; 
      left++; 
      reverse_index--; 
     } 
     right++;   
     left = right; 
    } 

    strrev(str); 
    return str; 
} 


//output is: dog red big the 

, wenn Sie keine strrev aus irgendwelchen Gründen haben, hier sind Sie

char* strrev(char *str) { 
    char *p1, *p2; 

    if (! str || ! *str) 
     return str; 

    for (p1 = str, p2 = str + strlen(str) - 1; 
          p2 > p1; ++p1, --p2) { 
     *p1 ^= *p2; 
     *p2 ^= *p1; 
     *p1 ^= *p2; 
    } 

    return str; 

}

auch freiere Art und Weise, aber langsam zu

char* strrev(char *str) { 
    int left = 0; 
    int right = strlen(str) - 1; 
    char tmp; 
    while(left < right) { 
     tmp = str[left]; 
     str[left] = str[right]; 
     str[right] = tmp; 
     left++; 
     right--; 
    } 

    return str; 
} 
+0

Ich hoffe, Ihnen ist bewusst, dass 'strrev()' nicht auf jeder Plattform verfügbar ist. Siehe [Ist die Funktion strrev() in Linux nicht verfügbar?] (Http://stackoverflow.com/questions/8534274/is-the-strrev-function-not-available-in-linux). – datell

+1

@datell hinzugefügt, handgemacht, für diesen Fall – bobra

+0

Großartig! Sie könnten die Plattform mit dem Präprozessor wie '#if definiert (__ MACH__) || überprüfen definiert (__ linux__) die benutzerdefinierte Implementierung # endif' oder etwas Ähnliches. Das wäre sehr * C-styled * ich denke, – datell

0
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 

#define SIZE 80 

char *reverseWords(char line[SIZE]){ 
    char temp[SIZE]; 
#if SIZE > 255 
    unsigned index[SIZE]; 
#else 
    unsigned char index[SIZE]; 
#endif 
    int i, index_count = 0; 
    int inside_word = !!isalpha((unsigned char)line[0]), word = inside_word; 

    for(index[index_count++] = i = 0; line[i]; ++i){//copy & make index table 
     unsigned char ch = temp[i] = line[i]; 
     if(inside_word && !isalpha(ch) || !inside_word && isalpha(ch)){//Edge 
      index[index_count++] = i; 
      inside_word = !inside_word; 
     } 
    } 
    index[index_count] = i; 

    int last_word_index = index_count - 1;//last index 
    last_word_index -= !word^(last_word_index & 1);//to word 

    char *p =line; 
    for(i = 0; i < index_count-1; ++i){ 
     int len; 
     if(word){ 
      len = index[last_word_index+1] - index[last_word_index]; 
      memcpy(p, &temp[index[last_word_index]], len); 
      last_word_index -= 2; 
     } else { 
      len = index[i+1] - index[i]; 
      memcpy(p, &temp[index[i]], len); 
     } 
     word = !word; 
     p += len; 
    } 

    return line; 
} 

int main(void){ 
    char str[SIZE]; 

    while(fgets(str, sizeof str, stdin)){ 
     printf("%s", reverseWords(str)); 
    } 
} 
+0

[DEMO] (http://ideone.com/VIglfa) – BLUEPIXY

0

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.

  1. 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
  2. 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.

  3. 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 wordnallocRet ü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.