11 Stimmen

Wie funktioniert dieser C-Code?

Ich habe mir den folgenden Code angesehen, auf den ich gestoßen bin, um eine Zeichenkette in umgekehrter Reihenfolge in C unter Verwendung von Rekursion zu drucken:

void ReversePrint(char *str) { //line 1
  if(*str) {                   //line 2
      ReversePrint(str+1);     //line 3
      putchar(*str);           //line 4
  }
}

Ich bin relativ neu in C und bin durch Zeile 2 verwirrt. *str ist nach meinem Verständnis eine Dereferenzierung des Zeigers und sollte den Wert der Zeichenfolge an der aktuellen Position zurückgeben. Aber wie wird dies als ein Argument für eine bedingte Anweisung (die außer einem booleschen Recht sollte?) verwendet werden? In Zeile 3 wird der Zeiger immer auf den nächsten Block inkrementiert (4 Bytes, da es sich um einen int handelt)... könnte dieser Code also nicht fehlschlagen, wenn sich im nächsten Speicherblock nach dem Ende der Zeichenfolge zufällig Daten befinden?

Update : Es gibt also keine booleschen Typen in C, richtig? Eine bedingte Anweisung wird als "falsch" ausgewertet, wenn der Wert 0 ist, und andernfalls als "wahr"?

0voto

ezpz Punkte 11421

Bedingte Anweisungen ( if , for , while usw.) erwarten einen booleschen Ausdruck. Wenn Sie einen ganzzahligen Wert angeben, läuft die Auswertung auf Folgendes hinaus 0 == false ou non-0 == true . Wie bereits erwähnt, ist das Abschlusszeichen einer c-Zeichenkette ein Null-Byte (Ganzzahlwert 0). Daher ist das if scheitert am Ende der Zeichenkette (oder am ersten Null-Byte innerhalb der Zeichenkette).

Nebenbei bemerkt, wenn Sie *str auf einen NULL-Zeiger rufen Sie ein undefiniertes Verhalten auf; Sie sollten immer überprüfen, ob ein Zeiger gültig ist, bevor Sie ihn dereferenzieren.

0voto

Shrinidhi Punkte 630

1.

str ist ein Zeiger auf ein Zeichen. Inkrementieren str wird der Zeiger auf das zweite Zeichen der Zeichenkette zeigen (da es sich um ein char-Array handelt). HINWEIS: Das Inkrementieren von Zeigern wird um den Datentyp erhöht, auf den der Zeiger zeigt.

Zum Beispiel:

int *p_int;
p_int++;     /* Increments by 4 */

double *p_dbl;
p_dbl++;      /* Increments by 8 */

2.

if(expression)
{
   statements;
}

Der Ausdruck wird ausgewertet, und wenn der resultierende Wert Null ist ( NULL , \0 , 0 ), werden die Anweisungen nicht ausgeführt. Da jede Zeichenkette mit \0 die Rekursion muss irgendwann enden.

0voto

Christoph Punkte 157217

C hat kein Konzept für boolesche Werte: In C kann jeder skalare Typ (d. h. arithmetische Typen und Zeiger) in booleschen Kontexten verwendet werden, in denen 0 bedeutet false und ungleich Null true .

Da Zeichenketten mit Nullen abgeschlossen werden, wird der Terminator wie folgt interpretiert false , während jedes andere Zeichen (mit einem Wert ungleich Null!) als true . Dies bedeutet, dass es eine einfache Möglichkeit gibt, über die Zeichen einer Zeichenkette zu iterieren:

for(;*str; ++str) { /* so something with *str */ }

StrReverse4() macht das Gleiche, aber durch Rekursion statt Iteration.

0voto

Versuchen Sie diesen Code, der genauso einfach ist wie der, den Sie verwenden:

int rev(int lower,int upper,char*string)
{
  if(lower>upper)
          return 0;
   else
          return rev(lower-1,upper-1,string);
}

0voto

Gaston Punkte 1798

Dies ist Art von Off-Topic, aber als ich die Frage sah, fragte ich mich sofort, ob das tatsächlich schneller als nur tun ein strlen und Iteration von der Rückseite war.

Also habe ich einen kleinen Test gemacht.

#include <string.h>

void reverse1(const char* str)
{
    int total = 0;
    if (*str) {
            reverse1(str+1);
            total += *str;
    }
}

void reverse2(const char* str)
{
    int total = 0;
    size_t t = strlen(str);
    while (t > 0) {
            total += str[--t];
    }
}

int main()
{
    const char* str = "here I put a very long string ...";

    int i=99999;

    while (--i > 0) reverseX(str);
}

Ich habe es zuerst mit X=1 (unter Verwendung der Funktion reverse1) und dann mit X=2 kompiliert. Beide Male mit -O0.

Bei der rekursiven Version dauerte es etwa 6 Sekunden, bei der strlen-Version 1,8 Sekunden.

Ich denke, das liegt daran, dass strlen in Assembler implementiert ist und die Rekursion einen ziemlichen Overhead bedeutet.

Ich bin mir ziemlich sicher, dass der Benchmark repräsentativ ist; sollte ich mich irren, korrigieren Sie mich bitte.

Wie auch immer, ich dachte, ich sollte das mit Ihnen teilen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X