C - String umkehren?

Dieses Thema im Forum "Softwareforum" wurde erstellt von spatze, 20. April 2008.

  1. Hallo alle zusammen,

    momentan bin ich dabei, C zu lernen und ackere gerade auch ein schönes Tutorial dafür durch (ist das Buch C Primer Plus... wem es vielleicht etwas sagt). Am Ende jedes Kapitels gibt es da auch so einige Übungsaufgaben, die man sich von sich aus bewältigen muss. Die Aufgabe, an der ich nun hänge, ist, einen String an eine Funktion zu übergeben, die den String in umgekehrter Folge zurückgibt. Und da hänge ich nun fest. Für mich sieht das alles richtig aus, ich weiß einfach nicht, wo der Fehler liegen könnte:

    Code:
    #include <stdio.h>
    #include <string.h>
    #define SIZE 20
    
    char *reverse(char *s);
    
    int main(void)
    {
    	char arr[SIZE] = "Hello!";
    	char *ptr;
    	ptr = reverse(arr);
    
    	printf("String reversed: %s\n", ptr);
    
    	return 0;
    }
    
    char *reverse(char *s)
    {
    	int x, y;
    	char s2[SIZE];
    
    	for(x = strlen(s), y = 0; x > 0, y < strlen(s); --x, y++)
    		s2[y] = s[x];
    	s2[strlen(s)] = '\0';
    	return s2;
    }
    Ich danke schonmal allen im Voraus für ihre Hilfe!
     
  2. SPEED notgeile Macke

    SPEED
    Registriert seit:
    2. März 2007
    Beiträge:
    40.721
    Mach mal bei der for Schleife: x >= 0, und y <= 6 oder x > -1 und y < 7

    ;)

    Ach ja ... Dein Array ist 6 char lang.
    Anfangen zu zählen aber bei 0!!!!

    Nur zur Erklärung!
     
  3. Hmm... also ich gehe die Funktion jetzt nochmal durch:

    Der String ist 'Hello!' und 6 chars lang. x ist der Rückwärtszähler für das an die Funktion übergebene Array (arr bzw. s in der Funktion), y der Vorwärtszähler für das Zielarray (s2). x bekommt am Anfang in der for-Schleife den Wert der Länge von 'Hello!', also 6, während y den Wert 0 bekommt. Meine Idee dahinter ist, dass er nun das letzte Element von s nimmt, also in diesem Fall das 5. (daher ja auch das --x) und dieses dem 1. Element von s2 (also s2[0]) übergibt. Dies geschieht solange, wie x > 0 ist und y < strlen(s) (also 6) ist [mir fällt grad auf, dass das doppelt gemoppelt ist, eine einzige Bedingung würde hier ausreichen). Nach der Schleife bekommt s2 noch eine \0 im Element, was der Stringlänge von s entspricht (hier: 6).
    Das hört sich für mich alles logisch an, deswegen weiß ich nicht, wieso das nicht klappt.
     
  4. Midian Nihilist

    Midian
    Registriert seit:
    17. März 2001
    Beiträge:
    1.788
    Ort:
    somewhere far beyond
    du gibst eine lokale Variable zurück ;)

    So gehts:


    Code:
    #include <stdio.h>
    #include <string.h>
    #define SIZE 20
    
    
    char *reverse(char *s);
    char s2[SIZE];
    
    int main(int argc, char *argv[])
    {
       char* test = "Hello!";
       memset(s2, 0, SIZE);
    
       printf("String reversed: '%s' !\n", reverse(test));
    
       return 0;
    }
    
    char *reverse(char *s)
    {
       int y = 0;
    
       for(int i = strlen(s)-1; i >= 0; i--, y++)
       s2[y] = s[i];
    
       //s2[strlen(s)] = '\0';
       return s2;
    }
    
     
    Zuletzt bearbeitet: 20. April 2008
  5. Danke, aber was ist memset? Das kenne ich noch gar nicht. Und die Adresse von s2 in der Funktion kann man so nicht zurückgeben? Ist es denn iwie möglich, die Funktion ohne Rückgabewert zu deklarieren (also void reverse(...)) ?
     
  6. ohne rückgabewert musst du mit pointern arbeiten und die adresse des arrays an die funktion übergeben. hab hier sogar noch eine alte lösung von einer übungsaufgabe aus der fh rumliegen:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    void reverse(char *s){
        char *links;            // char-Pointer der auf das linke Zeichen zeigt
        char *rechts;           // char-Pointer der auf das rechte Zeichen zeigt
        
        links = &s[0];
        rechts = &s[strlen(s)-1];
           
            while (links<rechts)    // solange der Pointer links "weiter links" bzw kleiner ist als der Pointer rechts
        {
            
            char merker;        // Hilfsvariable die sich den Buchstaben merkt
            merker = *rechts;   // merker "merkt" sich das Zeichen auf das rechts zeigt
            *rechts = *links;   // das Zeichen auf das rechts zeigt wird durch das Zeichen
                                // auf das links zeigt ersetzt
            *links = merker;    // das Zeichen auf das links zeigt wird durch das Zeichen in merker ersetzt
            
            links++;            // den Pointer links um eine Stelle nach rechts verschieben
            rechts--;           // den Pointer rechts um eine Stelle nach links verschieben
        }             
    }
    
    int main(void)
    {
        printf("Zeichenkette umkehren\n\n");
        
        char s[200];
        printf("Eingabe:");    
        
        fgets(s,200,stdin);     // Einlesen der Eingabe
        s[strlen(s)-1] = 0;     // \n von fgets eliminieren    
        
        reverse(s);
        
        printf("Die Eingabe umgedreht: %s\n",s);
        
        system("PAUSE");
        return 0;
    }
    
     
  7. Okay, das klappt. Danke an alle für ihre Antworten!

    Nun stehe ich aber vor einem neuen Problem, vielleicht könnt ihr mir da auch wieder weiterhelfen: Es soll eine Funktion erstellt und getestet werden, mit dem die Leerzeichen von einem String eliminiert werden sollen. Hierzu kann man eine gewisse Anzahl Strings eingeben, die in einem zweidimensionalen Array gespeichert werden.
    Wie ich die Leerzeichen entfernen kann, weiß ich schon. Das Problem ist nur, dass ich nciht weiß, wie ich die Funktion deklarieren soll. Eine ähnliche Aufgabe steht auch schon im Buch drin, aber ich weiß nicht ganz, was da vonstatten geht.

    Dem zweidimensionalen Array, das die einzelnen Strings speichert, habe ich die Größen [4][21] zugeteilt. Das eindimensionale Array, welches die Strings, die der User eingibt, an das zweidimensionale Array weitergibt, hat dann auch die Größe [21] (mit max. 20 chars).

    Nun sollen die Strings nacheinander in das eindimensionale Array eingelesen werden und an das erste Array von Elementen weitergegeben werden. Dazu habe ich folgendes geschrieben:
    Code:
    printf("Enter up to %d strings: \n", MAXSTR); //MAXSTR = 4
    	while(c < MAXSTR && gets(temp))              //c = 0
    	{
    		delspc(tarr[c], temp); 
                    /*delspc = Funktion    
                       tarr[MAXSTR][MAXLEN] = zweidimensionales Zielarray*/
    		c++;
    	}
    Meine Funktionsdeklaration sieht momentan so aus:
    Code:
    void delspc(char (*s1)[MAXLEN], char *s2);  //MAXLEN = 21
    
    Der Pointer der Funktion würde somit im ersten Durchlauf der while-Schleife auf tarr[0] zeigen. Damit könnte ich dann in der Funktion die einzelnen Werte vom eindimensionalen String übernehmen, indem ich den Pointer dereferenziere:
    Code:
    *(s1 + count) = s2[count2]; //Pointer befindet sich in for-Schleife
    Leider klappt das alles nicht so gut, wie ich mir das ausgemalt habe, Visual Studio gibt mir nur mehrere Error zurück (error C2664: 'delspc' : cannot convert parameter 1 from 'char [16]' to 'char (*)[16]'). Irgendwo habe ich garantiert wieder einen Denkfehler bei den Pointern bzw. bei der Arrayübergabe an den Pointer.
     
    Zuletzt von einem Moderator bearbeitet: 20. April 2008
  8. SPEED notgeile Macke

    SPEED
    Registriert seit:
    2. März 2007
    Beiträge:
    40.721
    Mein Fehler!
    Sry. :uff:

    Hab übersehen dass ja --x und ++y steht.
     
  9. Silent Hunter Bambis Alptraum

    Silent Hunter
    Registriert seit:
    7. März 2000
    Beiträge:
    27.040
    Eine Library-Funktion, die einen Speicherbereich auf den angegebenen Wert setzt (und dabei oft um Größenordnungen schneller ist, als wenn man es "per Hand" macht.

    Kannst Du schon. Nur hört s2 in dem Moment auf zu existieren, in dem die Funktion verlassen wird - die Adresse ist also vollkommen wertlos.

    Du mußt den String in einem Speicherbereich zurückgeben, der auch nach dem Verlassen der Funktion noch weiter am Leben bleibt - Möglichkeiten gibt es da viele.

    Oder Du drehst einfach den String in dem Speicherbereich um, in dem er der Funktion übergeben wird (Bonuspunkte gibt's wenn Du das ohne eine temporäre Variable schaffst hrhr. :madcat: )
     
  10. Ah okay, das hört sich aber alles nach höherer Wissenscahft an wozu ich mich momentan noch nicht berufen fühle. ^^
     
  11. kullerhamPster [me] Nager

    kullerhamPster [me]
    Registriert seit:
    6. März 2000
    Beiträge:
    17.191
    Wie ist denn "tarr" definiert?

    btw: Muss immer wieder feststellen, dass es in C Sachen gibt, von denen ich bisher schlicht nix wusste.

    So was wie dieses int (*p)[5] ist mir heute glaub' zum ersten Mal begegnet. Sieht rein syntaktisch imo auch nicht sehr hübsch aus.
     
    Zuletzt bearbeitet: 20. April 2008
  12. tarr soll bei mir das zweidimensionale Zielarray darstellen, hab wohl vergessen, das näher zu erläutern.

    (*p)[5] wär in diesem Fall nen Pointer auf ein Array mit 5 char-Elementen (tarr ist bei mir tarr[5][21], daher steht in der Deklaration: (*s1)[MAXLEN] (MAXLEN ist als 21 definiert)). Alternativ könnte man auch p[][5] oder [][5] (in der Deklaration umindest) schreiben.
     
    Zuletzt von einem Moderator bearbeitet: 20. April 2008
  13. Mit der Klammerung hätte ich auf den ersten Blick auch eher einen Funktionszeiger bzw. ein Array von selbigen vermutet. Auf den zweiten fällt dann doch die fehlende Argumentliste auf.

    Ansonsten fällt mir oben auf:
    links = &s[0] ist eher umständlich. s ist ein Array, ergo effektiv ein Zeiger auf das erste Element. Umgeschrieben steht da also &(*(s+0)) (auf den Zeiger wird 0 addiert, dann dereferenziert, dann wieder die Adresse genommen). Da reicht auch ein schlichtes links = s (und entsprechend rechts = s + strlen()-1).

    delspc(char (*s1)[MAXLEN], char *s2);

    cannot convert parameter 1 from 'char [16]' to 'char (*)[16]')

    Der Compiler will dir damit sagen, daß entweder das * oder das [MAXLEN] zu verschwinden hat, denn wenn tarr als char tarr[x][y] deklariert ist, dann ist tarr eben ein char[] und kein char*[]. Entweder delspc(char*, char*) oder delspc(char[], char*), aber nicht beides auf einmal.
     


  14. Jo, danke für den Denkanstoß, bin jetzt auch drauf gekommen, dass das [MAXLEN] aus der Deklaration verschwinden muss, schließlich zeigt der Pointer nacheinander (durch die while-Schleife) auf die verschiedenen Arrays von tarr, die die Strings beinhalten.

    Danke, wieder was dazugelernt. :p
     
  15. kullerhamPster [me] Nager

    kullerhamPster [me]
    Registriert seit:
    6. März 2000
    Beiträge:
    17.191
    Öh, aus der Deklaration von delspc? Weiß grade nicht, ob ich das logisch finden soll, bin aber zu unmotiviert, darüber nachzudenken. :ugly:

    EDIT: Hm, Triencos Erklärung klingt logisch. Andererseits willst Du aber doch einen Pointer auf einzelne Zeilen Deines 2D-Arrays übergeben, insofern sollte dieses Konstrukt da doch eigentlich passen :confused:
    Und eigentlich sollte ein
    int * p
    und ein
    int (*p)[5] doch fast das Gleiche sein, nur dass im zweiten Fall der Compiler die Größe des Arrays kennt und somit im ersten Fall ein (p+1) den Pointer auf das nächste Element des Arrays bewegt, im zweiten auf die nächste Zeile.
    Oder bin ich grade komplett von der Rolle?
     
    Zuletzt bearbeitet: 21. April 2008
  16. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    int* p ist ein Zeiger auf int, int (*p)[5] ist ein Array von 5 Zeigern auf int.
    Wird nun ein Element des Arrays p mit p[x] ausgewählt, so ist dessen Typ Zeiger auf int (int*).

    delspc arbeitet mit einem einzelnen String, nicht mit einem Array von Strings, also muss der Typ des Parameters char* sein.
     
  17. kullerhamPster [me] Nager

    kullerhamPster [me]
    Registriert seit:
    6. März 2000
    Beiträge:
    17.191
    Nee, soweit ich das verstanden hatte, ist int (*p)[5] eben kein Array von 5 Zeigern, das wäre int * p[5], also ohne Klammer.
    Siehe hier: http://home.netcom.com/~tjensen/ptr/ch8x.htm (ganz unten auf der Seite).

    Ich kannte das vorher auch nicht, deswegen bin ich ja so verwirrt ;)
     
  18. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    Stimmt, ist tatsächlich was anderes.
    Dann müsste der TE beim Aufruf von delspc &tarr[c] statt tarr[c] schreiben.
     
  19. Bloß nicht. Wenn tarr ein Zeiger auf ein Array ist (effektiv char**), dann bekommt er damit nicht die c-te Stelle im Array, sondern den c-ten Zeiger nach dem Zeiger auf den Anfang des Arrays. Davon abgesehen, daß damit eigentlich alles schiefgehen müßte, muß erst der Zeiger dereferenziert werden, bevor irgendein Index sinnvoll ist. (*tarr)[c] ist dann aber ein char. &((*tarr)[c]) ist zwar wieder ein char*, aber bringt nicht viel.

    Für die totale Verwirrung deklariert jetzt bitte noch jemand ein Array auf 5 Funktionszeiger, die einen int-Zeiger zurückgegeben und einen Zeiger auf ein Array als Parameter bekommen *muahaha*
     
  20. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    Das funktioniert schon wenn tarr als char tarr[x][y] definiert ist. Mann muss allerdings, wie du bereits geschrieben hast, in der Funktion selbst mittels (*p)[index] auf die Array-Elemente zugreifen (ja, ich hab's ausprobiert). Das ist allerdings ziemlich :ugly:, deshalb macht man sowas nicht.
     
  21. In solchen Momenten lehnt man sich entspannt zurück und denkt sich "zum Glück muß man so einen Krampf in C++ nicht mehr mitmachen" ,-)
     
  22. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    :hoch:

    Wahre Worte. Zu viele Leute machen's aber mit C++ genauso, weil sie's nicht besser wissen. Die müssen dann mit Java gezwungen werden ;-)
     
  23. Hallo Leute,

    nach so viel Verwirrung in meinem Thread wollte ich auch nochmal kurz meinen Senf zu diesem ganzen Pointer-Gewurschtel dazugeben. :ugly:

    kullerhampster hat das so weit richtig beschrieben. In meinem Beispiel mit tarr[4][21] muss man die Funktion mit delspc(char *s1, char *s2) deklarieren.
    Folgende Schreibweisen sind für Pointer richtig:

    (*ptr)[21] - Der Pointer zeigt in diesem Fall auf ein Array, das 21 chars beinhaltet (Alternativschreibweise: ptr[][21])
    *ptr[21] - Es wird ein Array von 21 Pointern erstellt

    In meinem Fall korrekt:
    Da ich ja per while-Schleife nacheinander mehrere temp-Arrays einlese, kann ich tarr[c] einfach direkt an die Funktion übergeben. Der Pointer der Funktion zeigt dann beispielsweise auf tarr[0] und kann so dereferenziert auf die einzelnen Werte (muss man natürlich auch nochmal in eine for-Schleife einbauen) des [21] Elemente großen Arrays der 2. Dimension zeigen, wo schlussendlich der String rüberkopiert wird.

    PS: Ich freu mich schon auf C++. :D
     
    Zuletzt von einem Moderator bearbeitet: 23. April 2008
  24. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    Ich frag mich immer noch, warum du dir diese Schreibweise antun willst. char*, falls du ein Array von chars übergeben willst, char**, falls du ein Array von char-Arrays übergeben willst und gut ist.

    Wo du höchstwahrscheinlich genauso programmierst, wie du es in C getan hast. Gibt es einen speziellen Grund, warum du nicht gleich mit C++ angefangen hast?
     
  25. Muß er ja jetzt nicht mehr. Nebenbei ist es bei solchen Konstrukten wesentlich lesbarer, wenn man den * dahin schreibt, wo er hingehört: zum Typen. Ein Int-Pointer ist eben int* und schon gibts keine Verwirrung mehr.

    Allerdings bin ich auch nicht sicher, ob es so eine gute Idee ist, sich erst von C "versauen" zu lassen, wenn man langfristig ohnehin zu C++ will. Seit ich da gesehen habe, was manch alte C-Hasen in C++ an Code verbrechen, würde ich das fast als kontraproduktiv sehen. Überall Arrays mit "hoffentlich ausreichender Größe", die STL wird sowieso nicht benutzt ("wat'n dat für'n komisches Zeuchs") und statt Templates gibts unwartbare Makrowüsten.
     
  26. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    Ich sehe, wir verstehen uns ;)
     
  27. In der Berufsschule gehen wir in derselben Reihenfolge vor. Und nur weil ihr C++ler nicht versteht, wie man Pointer in C deklariert/übergibt, ist es nicht unbedingt verkehrt zu wissen, wie das funktioniert ;)

    Ruecki: Natürlich ist das mit dem **char auch möglich, aber mit *char[...] hat den Vorteil, dass man auf einem Blick sieht, wie groß das jew. Array ist.
     
    Zuletzt von einem Moderator bearbeitet: 24. April 2008
  28. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    Ach so, du wirst gezwungen...

    Ich denke, wir haben dir hier bereits das Gegenteil bewiesen. Wir wissen's offenbar besser als du :D

    Die explizite Längenangabe bringt dir recht wenig, da du dich nicht auf sie verlassen kannst und darfst. Es werden auch Arrays mit abweichender Länge als Parameter akzeptiert (der VC-Compiler gibt noch nicht mal eine Warnung aus).

    €: OK, habs mir nochmal angeschaut, musste mir die Disassembly ansehen, um den Unterschied zwischen char** und char[][15] zu erkennen:uff:

    "Echte" mehrdimensionale Arrays werden tatsächlich am Stück im Speicher abgelegt, ohne die zusätzliche Verzeigerung. Man kommt also beim Zugriff mit einer Dereferenzierung aus. Die Adresse des Elements array[j] einer mit char array[X][Y] deklarierten Variable ist daher (array + i*Y + j). Damit der Compiler auch in der Funktion die korrekte Adresse berechnen kann, muss man zumindest die Länge Y der zweiten Dimension mitangeben, also char[][Y] oder auch char(*)[Y]. Wieder was gelernt, werde ich trotzdem niemals verwenden :D
     
    Zuletzt bearbeitet: 24. April 2008


  29. :D

    Wie ich auch schon gesagt/geschrieben habe, ist vielleicht ganz gut zu wissen, aber wenn ich in C++ auf sowas nicht angewiesen wäre, würd ichs auch weglassen. :p
     
  30. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    Man ist auch in C nicht darauf angewiesen. Dass ich das noch nicht verwendet habe liegt aber wohl eher daran, dass ich (persönlich) in der Praxis keine Arrays habe, deren Länge statisch, also bereits bekannt ist, bevor das Programm ausgeführt wird.

    €: Und im Endeffekt wusste ich nur nicht, dass char(*)[LEN] mit char[][LEN] identisch und was anderes als char*[LEN] ist.
     
    Zuletzt bearbeitet: 24. April 2008
  31. Golvellius gesperrter Benutzer

    Golvellius
    Registriert seit:
    29. Januar 2003
    Beiträge:
    3.975
    Ort:
    Valley of Doom
    Dann kann man allerdings auch nicht mehr mehrere Variablen in einem Abwasch deklarieren, wie z.B. int *bla, *blubb, *blublub. Die Tatsache, dass z.B. int* bla, blubb, blubub nicht geht wenn man drei Pointer haben will, deutet irgendwie darauf hin dass der * vielleicht doch eher "dazwischen"gehört. Ich stimme dir allerdings trotzdem zu.

    lol, köstlich :lol:
     
  32. Auch wieder wahr. Wobei gerade bei C/C++ oft Bequemlichkeit und Performance vor Sauberkeit kommen. Die Methode hat halt den Vorteil, daß man prinzipiell ganz verschiedene Dinge in einer Zeile deklarieren kann (obwohl es ja immer heißt, man soll sowas lassen):

    int x, *p, a[10];

    Und Bonuspunkte gibts für dieses häßliche Ding:

    int* (*f[5])( int (*a[])[] );
     
  33. Das ist wirklich zu hardcore. :ugly:

    Das schöne an Programmiersprachen ist dass man immer weider etwas neues hinzulernen kann gelle. :D
     
  34. Ruecki Frei und schwerelos

    Ruecki
    Registriert seit:
    28. Februar 2007
    Beiträge:
    565
    Das muss auch so sein, nicht nur beim Programmieren.
     
  35. int* (*f[5])( int (*a[])[] );

    Heut früh war mir noch total klar, was ich damit haben wollte. Jetzt kommen mir erste Zweifel. Sollte eigentlich ein Array von Zeigern auf Funktionen sein (*f[5]), die einen int* zurückliefern und als Parameter ein Array von Arrays auf Zeiger... oder so... ohne Klammerung muß man immer im Kopf haben, was stärker "bindet".

    Unschön ist es erst, wenn jemand beschließt, daß er keine Lust mehr hat, etwas dazuzulernen, weil er denkt, er weiß schon genug ,-) Da greift dann schnell die Redewendung "wenn mal als einziges Werkzeug einen Hammer hat, dann sieht jedes Problem aus wie ein Nagel".
     
Top