[C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

Das Palindrom-Beispiel hatten wir schon mal, allerdings habe ich jetzt einen ganz neuen Lösungsansatz (der auch bereits einwandfrei funktioniert, wenn ich jedes Wort in ein eigenes char array stecke). Doof nur, dass ich nur dann überhaupt eine Ausgabe bekomme, wenn ich diese wie im auskommentierten Teil in der main mache. Wenn sie so umgesetzt wird, wie im Code blau markiert, bekomme ich eine Windows Fehlermeldung. Dabei hat exakt diese Ausgabe bei der anderen Version des Programms super funktioniert ... Why?
Code:
#include <stdio.h>

int checkLength(const char *s) {
    int length=0;

    while (*(s+length) != '\0') {
        length++;
    }

    return length;
}

int checkPalindrome(char *s) {
    int i=0, j=0, length=checkLength(s);
    char firstHalf[50]={0}, secondHalf[50]={0}; 
    
    [COLOR=seagreen]/* Wandle alle Großbuchstaben in Kleinbuchstaben um. */    for (i=0; i<length; i++) {
        if (*(s+i) >= 'A' && *(s+i) <= 'Z') {
            *(s+i)+=32;
        }
    }

    [COLOR=seagreen]/* 
    Zerlege das zu überprüfende Wort in 2 Hälften und speichere jede in einem eigenen string.
    Der string, der die zweite Worthälfte bekommt, muss verkehrt herum angefüllt werden. 
    */    for (i=0, j=(length-1); i<((length-1)/2), j>((length-1)/2); i++, j--) {
        *(firstHalf+i)=*(s+i);
        *(secondHalf+i)=*(s+j);
    }

    [COLOR=seagreen]/* Vergleiche die beiden Strings. */    i=0;
    while (*(firstHalf+i) != '\0' || *(secondHalf+i) != '\0') {
        if (*(firstHalf+i) != *(secondHalf+i)) {
            return 1;
        }
        i++;
    }

    return 0;
}

int main(void) {
    int i=0;
   [COLOR=royalblue][B] char *s[] = {"Hannah", "Smartphone", "Retsinakanister", "Lagerregal", "programmieren", 0 };

    while (*(s+i) != '\0') {
        if (checkPalindrome(*(s+i))==0) {
            printf("%s is a Palindrome!\n", *(s+i));
        } else {
            printf("Sorry, %s isn't a Palindrome.\n"[/B][COLOR=royalblue][B][COLOR=royalblue][B], *(s+i)[/B]);
        }
        i++;
    }[/B]
  [COLOR=seagreen]  /*
    [COLOR=seagreen][COLOR=royalblue][COLOR=seagreen]char *s1 = "Hannah", *s2 = "Smartphone", *s3 =  "Retsinakanister", *s4 = "Lagerregal", *s5 = "programmieren";    

    if (checkPalindrome(s1)==0) {
        printf("%s is a Palindrom!\n", s1);
    } else {
        printf("%s isn't a Palindrom.\n", s1);
    }
    if (checkPalindrome(s2)==0) {
        printf("%s is a Palindrom!\n", s2);
    } else {
        printf("%s isn't a Palindrom.\n", s2);
    }
    if (checkPalindrome(s3)==0) {
        printf("%s is a Palindrom!\n", s3);
    } else {
        printf("%s isn't a Palindrom.\n", s3);
    }
    if (checkPalindrome(s4)==0) {
        printf("%s is a Palindrom!\n", s4);
    } else {
        printf("%s isn't a Palindrom.\n", s4);
    }
    if (checkPalindrome(s5)==0) {
        printf("%s is a Palindrom!\n", s5);
    } else {
        printf("%s isn't a Palindrom.\n", s5);
    }
    putchar('\n');
    */
    return 0;
}
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

mhh, verstehe ich auch nicht, bei mir funktionieren sogar beide deiner "main's" nicht.
Viual Studio meckert was von einem verletzten kritischen Bereich, also hab ich mal probiert, wie's funktionieren könnte, und das kam raus:
Code:
int checkPalindrome(char *[COLOR=blue]S) {
[COLOR=blue]    char s[50];
    strcpy(s,S); //kopiert den Array S in s    int i=0, j=0, length=checkLength(s);     char firstHalf[50]={0}, secondHalf[50]={0};
    
    [COLOR=seagreen]/* Wandle alle Großbuchstaben in Kleinbuchstaben um. */
    //...    }
So läuft das Programm bei mir durch, und erkennt die Palindrome :). Warum man nicht den an die Funktion übergebenen String benutzen darf/kann, ist mir suspekt. Muss etwas mit dem Array von char Arrays zu tun haben. Denn ob man (wie oben) den 2.String in checkPalindrome(), oder schon in der main() ist egal:
Code:
checkPalindrome() jetzt wieder normal, aber "Umwandlung" schon in der main()
int checkPalindrome(char *[COLOR=blue]s) {
[COLOR=blue]     int i=0, j=0, length=checkLength(s);     char firstHalf[50]={0}, secondHalf[50]={0};    
    [COLOR=seagreen]/* Wandle alle Großbuchstaben in Kleinbuchstaben um. */
    //...}
int main(void) {
    int i=0;
    
    char *s[] = {"Hannah", "Hanah", "Retsinakanister", "Lagerregal", "programmieren", 0 };

    while (*(s+i) != '\0') {
[COLOR=blue]        char str[100];
        strcpy(str,*(s+i));        if (checkPalindrome([COLOR=blue]str)==0) {
            printf("%s is a Palindrome!\n", *(s+i));
        } else {
            printf("Sorry, %s isn't a Palindrome.\n", *(s+i));
        }
        i++;
    }
}
Zur Bestimmung der Länge, und zur Umwandlung in Klein, bzw. Großbuchstaben, gibts in C schon ein paar Standardfunktionen:
Die Länge eines Strings gibt strlen(s) zurück, char in Kleinbuchstaben geht mit c = tolower(c), und in Großbuchstaben mit c = toupper(c)
Allerdings immer nur einen einzelen char, man muss sich also eine Schleife für ein Array basteln:
Code:
for (i=0; i<length; i++) {
    *(s+i) = tolower(*(s+i)) //Würde s[i] anstatt *(s+i) nicht übersichtlicher aussehen :) ?
}
noch schneller gehts so:
Code:
    int checkPalindrome2(char * S)
    {
    //Bestimmung der Länge
    const int length = strlen(S);
    //Notwendiger Zweit-String
    char s[50];
    //String2 wird String1 kleingeschrieben
    for(int i = 0; i <= length;i++)
        s[i] = tolower(S[i]);

    //nach Palindrome suchen: abbrechen, wenn vorderer Buchstabe nicht dem hinteren Buchstaben entspricht
    for(int i = 0; i < length/2; i++)
        if(s[i] != s[(length-1)-i]) return 1;
    
    //wenn nicht abgebrochen wurde, wars ein Palindrome!
    return 0;
    }
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

hehe, sowas hab ich auch letztens in LISP gesehn - war ne recht simple lösung: den basisstring umkehren und diese beiden dann vergleichen - feddich ^^

Code:
#include <stdio.h>

int checkLength(const char *s) {
    int length=0;
     while (*(s+length) != '\0') length++;
    return length;
}

[COLOR=seagreen]/* Wandle alle Großbuchstaben in Kleinbuchstaben um. */char* unshift(char* s) {
     int i;
    for (i=0; i<length; i++) {
        if (*(s+i) >= 'A' && *(s+i) <= 'Z') *(s+i)+=32;
    }
    return s;
}

[COLOR=seagreen]/* String umkehren. */char* reverseString(char *s) {
    int i, j, length=checkLength(s);
    char reverse[length]; // geht das? ^^ oder muss das ne konstante sein?

    [COLOR=seagreen]/* kehre den string um */    for (i=0; i<length; i++) *(reverse+length-1-i)=*(s+i); // i<length - nich lenght-1 ^^

    return reverse;
}

int main(void) {
    int i=0;
     char *s[] = {"Hannah", "Smartphone", "Retsinakanister", "Lagerregal", "programmieren", 0 }; // müsste man am ende nich '\0' schreiben statt ner ollen 0? ^^

    while (*(s+i) != '\0') {
        if (unshift(*(s+i)) == reverse(unshift(*(s+i)))) printf("%s is a Palindrome!\n", *(s+i));
        else printf("Sorry, %s isn't a Palindrome.\n", *(s+i));
        i++;
    }
}
geht recht simpel und der vorteil is: man brauch sich ned wegen der "mitte" sorgen machen (gerade bei ungerade-langen strings).
 
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

@ Freddycbv
Bibliotheksfunktionen sollen unbedingt vermieden werden, laut Aufgabenstellung. Dass es für fast alles was gibt, weiß ich eh. ;)

Muss wohl wirklich irgendwie daran liegen, dass sich checkPalindrom() nicht auskennt, welches array denn nun genau übergeben wird. Werde deine Idee, jedes Wort zuerst in ein eigenes array zu speichern, gleich einmal ausprobieren. Melde mich dann, ob's bei mir auch zum Erfolg geführt hat. Wenn es wirklich daran liegt, hätte ich aber gerne noch von irgendjemandem eine Erklärung ...
Besonders in Hinblick auf die Tatsache, dass es im alten Code anstandslos funktioniert.

@ DarkMo
Jo, der Ansatz ist so simple und doch clever, dass ich gleich dachte, das muss ich ausprobieren. Ist mir übrigens beim Call of Duty 4 Zocken eingefallen. :D

PS: Du kannst nicht length (ist ja aufgrund der unterschiedlichen Wörter unterschioedlich lange bzw. "variabel") als Größenangabe nehmen. Muss, wie du dir schon gedacht hast, ein konstanter Wert sein. Deswegen habe ich auch [50] angenommen, bei den beiden neuen strings.

[EDIT]
Freddycbv, du hattest recht! Das jeweils an checkPalindrome() übergebene Wort muss wirklich erst lokal in einem eigenen string gespeichert werden, bevor damit gearbeitet werden kann. Habe genau so einen neuen string eingefügt und plötzlich funktioniert's absolut perfekt! Jetzt hätte ich, wie gesagt, aber gerne von irgendjemandem die Erklärung dafür ...
Code:
#include <stdio.h>

int checkLength(const char *s) {
    int length=0;

    while (*(s+length) != '\0') {
        length++;
    }

    return length;
}

int checkPalindrome(char *s) {
    int i=0, j=0, length=checkLength(s);
    char [COLOR=royalblue][B]checkString[50]={0}[/B], firstHalf[25]={0}, secondHalf[25]={0}; 
    
    [COLOR=seagreen]/* Speichere das jeweils übergebene Wort in checkString */    [B][COLOR=royalblue]for (i=0; i<length; i++) {
        *(checkString+i)=*(s+i);
    }[/B]
    
    [COLOR=seagreen]/* Wandle alle Großbuchstaben in Kleinbuchstaben um. */    for (i=0; i<length; i++) {
        if (*(checkString+i) >= 'A' && *(checkString+i) <= 'Z') {
            *(checkString+i)+=32;
        }
    }

    [COLOR=seagreen]/* 
    Zerlege das zu überprüfende Wort in 2 Hälften und speichere jede in einem eigenen string.
    Der string, der die zweite Worthälfte bekommt, muss verkehrt herum angefüllt werden. 
    */    for (i=0, j=(length-1); i<((length-1)/2), j>((length-1)/2); i++, j--) {
        *(firstHalf+i)=*(checkString+i);
        *(secondHalf+i)=*(checkString+j);
    }

    [COLOR=seagreen]/* Vergleiche die beiden strings. */    i=0;
    while (*(firstHalf+i) != '\0' || *(secondHalf+i) != '\0') {
        if (*(firstHalf+i) != *(secondHalf+i)) {
            return 1;
        }
        i++;
    }

    return 0;
}

int main(void) {
    int i=0;
    char *s[] = {"Hannah", "Smartphone", "Retsinakanister", "Lagerregal", "programmieren", 0 };

    while (*(s+i) != '\0') {
        if (checkPalindrome(*(s+i))==0) {
            printf("%s is a Palindrome!\n", *(s+i));
        } else {
            printf("Sorry, %s isn't a Palindrome.\n", *(s+i));
        }
        i++;
    }

    return 0;
}
PS: 0 oder NULL am Ende des strings mit den Wörtern sollte stimmen. Funktioniert ja damit, wenn sonst keine Fehler im Code sind.
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

PS: Du kannst nicht length (ist ja aufgrund der unterschiedlichen Wörter unterschioedlich lange bzw. "variabel") als Größenangabe nehmen. Muss, wie du dir schon gedacht hast, ein konstanter Wert sein. Deswegen habe ich auch [50] angenommen, bei den beiden neuen strings.

stimmt, das problem hatte ich bei meinem open gl ding auch, wo ich arrays variabler länge bauen musste um die an die gpu zu senden ^^ geht ganz easy:
Code:
int length = checkLength(s);
char *reverse;
reverse = new char[length];
// zugriff dann einfach mit
reverse[index] = 'C'; // oder so
ich weis nur ned, ob das c is, glaub das is c++ (un kA obs bur c sein durft).

PS: 0 oder NULL am Ende des strings mit den Wörtern sollte stimmen. Funktioniert ja damit, wenn sonst keine Fehler im Code sind.
joa eben, NULL oder eben \0 hätt ich ohne weiteres geglaubt, aber das ne simple integer 0 geht, hätt ich nich gedacht ^^
 
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

Ich hab ne weitere Lösung gefunden, um das komische Array - Problem zu lösen (und habe auch eine Vermutung, worans liegen könnte ;))
Code:
int main(void) {
        int i=0;
        [COLOR=blue]char s[6][16] = {"Hannah", "Hanah", "Retsinakanister", "Lagerregal", "programmieren", 0 };
        while ([COLOR=blue]*(s+i)[0] != 0) {
            if (checkPalindrome(*(s+i))==0) {
                printf("%s is a Palindrome!\n", *(s+i));
            } else {
                printf("Sorry, %s isn't a Palindrome.\n", *(s+i));
            }
            i++;
        }
    return 0;
}
Ich denke, durch den Zeiger auf ein ("undefiniert großes" (was es ja eigentlich nicht ist, aber die Klammern [] sind ja leer )) Array, kommt es entweder zu Speicherproblemen (Zeiger bedeutet nicht, dass da auch unbedingt freier Speicherplatz dahinter steht!), oder er verwursctelt sich irgendwie zwischen den einzelnen Arrays. (Vielleicht gibts je einen, der hier genau weiß, was auf niedrigerer ebene abläuft, und warum dann der Computer streikt :))
Naja, durch char s[6][16] weiß das Programm ganz genau, wo es dran ist, und so läufts jetzt auch ohne irgenwelche Array-Schieberreien :)
 
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

^^ Dynamische Speicherverwaltung mit malloc, calloc und free ist mir genauso bekannt, wie Matrizen, aber beides sollte für dieses Beispiel nicht verwendet werden, weil wir zu dem Zeitpunkt, als das Hausübung war (vor ca. 1.5 Jahren) in C noch nicht bei diesen Dingen waren. Da kannten wir gerade mal Schleifen, statische Felder und Zeiger. ;)

Ich kenne zwar jetzt fast alles in C, aber ich will die Programme trotzdem nur mit den Dingen noch einmal machen, die ich damals zur Verfügung hatte.

Was mich im Moment am meisten interessiert, ist, wieso das ...
Code:
char *s[] = {"Hannah", "Smartphone", "Retsinakanister", "Lagerregal", "programmieren", 0 };
... in der ersten Version des Programms einwandfrei funktioniert, OHNE, dass ein zusätzlicher Zeichen-string für das jeweilige Wort angelegt werden hätte müssen.

^^ Bis mir das wer verraten kann, will ich probieren, ein bisschen an der Effizienz unseres jetzigen Ansatzes zu arbeiten. 4 Schleifen sind wohl nicht nötig?! Vielleicht lassen sich ein paar Dinge kombinieren. Mal schauen. :)
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

Darum :):
Code:
    /* Wandle alle Großbuchstaben in Kleinbuchstaben um. */
    for (i=0; i<length; i++) {
        if (*(s+i) >= 'A' && *(s+i) <= 'Z') {
            [COLOR=blue]*(s+i)+=32;        }
    }
Du veränderst den inhalt des übergebenen Arrays, dass hast du bei der 1.Version nicht gemacht. (aber bis man darauf kommt ... )
Durch die Änderung der Arrays verletzt das Programm aktiv (weil du jetzt ja schreibst, und nicht mehr liests) gegen das Speichermanagement, und das Programm stürzt ab.
Ich bin mir grad wirklich nicht sicher, aber ich vermute, dass char *s[] ={"...","...","..",...} den Speicher nicht richtig reserviert :ugly:
Ich bin echt froh, mittlerweile std::string und die Containerklassen zu benutzen, weil wenn man den Pointer-Kram nicht zu 100% richtig drauf hat, passieren einem immer wieder so doofe Fehler, von denen man garnet wirklich weiss, was man falsch gemacht hat :D
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

an und für sich kann das ja nur eine abfolge von chars "speichern". aber hier will man ne abfolge einer abfolge von chars speichern - quasi. ein char dürfte 8Bit groß sein (unicode 16) und wenn du da jetzt char *s[5] machst, sucht er sich 40Bit freien speicher und "belegt"/reserviert ihn. der zeiger zeigt nun also auf adresse 1427463 im speicher und das wäre s[0] szs. um auf dieses ominöse *(s+1) (s[1] geht bestimmt auch), zählt er nun also nochmal 4 bit druff und liest dann also ab adresse 1427463 + sizeof(char) = 1427463 + 8 = 1427471. wenn du jetzt da drin "abc" und "def" speicherst, würde er ja schon den speicherbereich sprengen. KANN aber natürlich auch sein, dass er dann 2 zeiger auf jeweils "abc" und "def" da reinspeichert. sofern der zeiger dann nich größer wie die 8bit sin, sollte es gehn. wenn du jetz allerdings diese zeigeradressen manipulierst, gehts freilich schief.

*(s+i) += 32;
gehn wir also davon aus: *s zeigt auf unsere adresse 1427463 und ist 40bit lang (also bis 1427503). nun schreiben wir "abc" an erste stelle und anstelle dieser charfolge bekommen wir wieder einen zeiger zu ihr. *s zeigt also auf die speicherzelle 1427463 an der der pointer auf speicherzelle 3946534 zeigt (da steht nun unser "abc"). "abc" geht also von 3946534 bis 394653 + (3*8) = 3946534 + 24 = 3946558.nun änderst du mit oben genannter zeile aber diesen pointer da um +32. er zeigt also quasi nun 8bit hinter unser "abc" was entweder ne speicherschutzverletzung auslöst oder eben dazu führt, dass er sagt "ausserhalb des arrays" oder sowas.


leider nur ne vermutung, vllt weis es einer genauer ><
 
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

char *s[5] machst, sucht er sich 40Bit freien speicher und "belegt"/reserviert ihn
Nein, auf einer 32bit Platform sucht er sich 5*32Bit = 20Byte, auf einer 64bit Platform sucht er sich 5*64bit = 40Byte. Warum?
char *s[5] ist ein Feld, das fünf Zeiger auf chars speichert. Kann man sich z.B. von cdecl (oder vermutlich im Repo eurer Distribution) erklären lassen.

Der Code lässt sich also "übersetzen" in:
Code:
char *s[5];
s[0] = "Hannah";
...
s[5] = 0;
Schön illustriert ist das z.B. hier www.fbi.h-da.de/fileadmin/personal/h.weber/Material_PG1/Kapitel_08.pdf , Kapietel 8.10.
Auch passend zum Thema: Galileo Computing :: C von A bis Z – 12.9 Zeiger auf Zeiger und Stringtabellen

Aber Achtung:
Code:
char *s = "Blubb";
kann ziemlich gefährlich sein, weil "Blubb" als Konstant betrachtet wird und somit unter Umständen in Speicher steht, der als nicht veränderbar markiert ist, korrekt wäre somit:
Code:
const char *s = "Blubb";

Beispielsweise crasht folgendes
Code:
        char *a = "Hallo";
        a[1] = 'e';
        printf("%s\n", a);
So merkt's dann auch der Übersetzer:
Code:
      const char *a = "Hallo";
        a[1] = 'e';
        printf("%s\n", a);

Siehe auch: https://www.securecoding.cert.org/c...rs+to+const+when+referring+to+string+literals
 
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

^^ Danke für die super Erklärung! Leuchtet ein. :daumen:

Indirekt hilft mir das zusätzliche Feld, in dem ich jetzt immer das zu überprüfende Wort speichere, bevor ich damit arbeite, sogar. So kann ich nämlich trotzdem in der main die ursprünglichen Wörter ausgeben lassen. Würde ich das genannte Feld ausgeben lassen, würden plötzlich nur noch Kleinbuchstaben, anstatt der originalen Wörter darstehen.

BTW: So könnte man die Funktion checkPalindrome() noch ein Bisschen verkürzen. Vielleicht ginge noch mehr durch weitere Schleifenverschachtelung, aber da stellt sich dann schon wieder die Frage, ob's der Effizienz/Performance noch helfen würde, oder nur mehr dem Erscheinungsbild des Codes.
Code:
int checkPalindrome(char *s) {
    int i=0, j=0, length=checkLength(s);
    char checkString[50]={0}, firstHalf[25]={0}, secondHalf[25]={0}; 
    
    [COLOR=seagreen]/*
    Speichere das jeweils übergebene Wort in checkString und wandle anschließend alle 
    Großbuchstaben in Kleinbuchstaben um. 
    */    for (i=0; i<length; i++) {
        *(checkString+i)=*(s+i);
        if (*(checkString+i) >= 'A' && *(checkString+i) <= 'Z') {
            *(checkString+i)+=32;
        }
    }

   [COLOR=seagreen] /* 
    Zerlege das zu überprüfende Wort in 2 Hälften und speichere jede in einem eigenen string.
    Der string, der die zweite Worthälfte bekommt, muss verkehrt herum angefüllt werden. 
    */    for (i=0, j=(length-1); i<((length-1)/2), j>((length-1)/2); i++, j--) {
        *(firstHalf+i)=*(checkString+i);
        *(secondHalf+i)=*(checkString+j);
    }
[COLOR=seagreen]
    /* Vergleiche die beiden strings. */    i=0;
    while (*(firstHalf+i) != '\0' || *(secondHalf+i) != '\0') {
        if (*(firstHalf+i) != *(secondHalf+i)) {
            return 1;
        }
        i++;
    }

    return 0;
}
@ DarkMo
Du hast ja irgendwo auf der Vorseite mal gemeint, das -1 bei length-1 könnte ich weglassen. Dann funktioniert allerdings der Code nicht mehr richtig (dann ist entweder alles Palindrom, oder nichts). Hast du dich getäuscht, oder habe ich dich falsch verstanden?

[EDIT]
Gerade ausprobiert: In den Abbruchbedingungen der Schleife könnte ich das -1 weglassen, beim Startwert von j allerdings nicht.
Code:
for (i=0, j=(length-1); i<(length/2), j>(length/2); i++, j--) {
^^ Kann das daran liegen, dass es für das Programm keinen Unterschied macht, ob das Wort gerade, oder ungerade ist (Anzahl der Buchstaben), weil die Schleife ja nur integer überprüft und da fällt ein möglicher Nachkommateil bei der Divison einer ungerade Zahl durch 2 sowieso weg?! Eine andere Erklärung habe ich dafür ATM nicht. Wenn das hier allerdings nur Glück und Zufall ist, lasse ich das -1 für die Logik lieber stehen ...

[EDIT2]
Habe was Interessantes weiter vorne im Thread ausgegraben:
5) Length = Anzahl Elemente, Text ist in str[0] bis str[length-1] enthalten. Wenn du mit i <= length arbeitest, liest du noch das Nullzeichen mit ein. Bei nicht-nullterminierten Strings schmiert dir dann das Programm mit nem Fehler ab (Speicherschutzverletzung oder ähnliches)
Wenn das stimmt und length die Nullterminierung beinhaltet, dann muss aber schon length-1 richtig sein, v.a. da ich ja von 0 weg zähle?!
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

Habe ein neues Beispiel, bei dem ich mir ziemlich sicher bin, dass es total easy zu programmieren ist, allerdings verstehe ich nicht so ganz, was passieren soll und warum. Auf gut Deutsch: Kann mir bitte jemand die Angabe erklären?! :ugly:

Capture.JPG

Ich hätte auch eine Lösung in C (nicht selbstgemacht), bei der ich aus programmiertechnischer Sicht fast alles verstehe:
Code:
#include <stdio.h>
#include <stdlib.h>

int isDigit([B][COLOR=darkorange]char s[/B]) {
    char *digits = "0123456789";
    int i=0;

    for (i=0; [B][COLOR=blue]*digits[/B];[COLOR=green] [B]digits++[/B], i++) {
        if (*digits==s) {
            return i;
        }
    }

    return -1;
}

int convert(char s[]) {
    int [COLOR=darkorchid][B]value=0[/B], minus=0, digit=0;
 
    /* Leerzeichen */
    while (*s==' ' || *s=='\t') {
        s++;
    }
 
    /* Vorzeichen */
    if (*s=='-') {
        minus=1;
        s++;
    } else if (*s=='+') {
        s++;
    }
 
    while (*s != '\0') {                
        digit = [COLOR=darkorange][B]isDigit(*s)[/B];
        if (digit==-1) { /* keine Ziffer */
            break;
        }
        value = [B][COLOR=darkorchid]value*10[/B] + digit; [COLOR=red][B]**[/B]        s++;
    }
         
    if (minus != 0) {
        return -value;
    }
         
    return value;
}

int main(void) {
    char *t1 = "012431234";
    char *t2 = "  -124";
    char *t3 = " +562";
    char *t4 = "13a";
    char *t5 = " a56 ";
    char *t6 = "     +13-56a";

    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t1, convert(t1), atoi(t1));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t2, convert(t2), atoi(t2));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t3, convert(t3), atoi(t3));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t4, convert(t4), atoi(t4));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t5, convert(t5), atoi(t5));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t6, convert(t6), atoi(t6));

    return 0;
}
^^ Die Teile im Code, die ich nicht verstehe, oder bei denen ich zwar eine Vermutung habe, mir aber nicht sicher bin, habe ich farblich markiert.

1. Orange: Soll das heißen, die Funktion isDigit, wird immer genau für die Stelle des char arrays aufgerufen, auf die der Zeiger *s gerade zeigt? Ich fände das irgendwie logischer, wenn in der Parameterdeklarationsliste von isDigit char *s stehen würde (ich weiß, dass damit das gesamt array übergeben werden würde). Dann könnte ich in isDigit den array-Inhalt mit dem dortigen array vergleichen.

2. Blau: Was soll denn das bitte für eine Abbruchbedingung sein? Ich habe testweise *digits!='\0' hingeschrieben, aber dann stimmt die Ausgabe des Programms nicht mehr. Auf was wird hier überprüft? Jedenfalls nicht auf die Nullterminierung.

3. Grün: Soll das wieder heißen, ich setze den Zeiger, der immer auf ein bestimmtes Element des arrays *digits zeigt, in jedem Schleifendurchlauf um eins weiter? Wieso dann nicht *digits++?

4. Violett: Wozu genau brauche ich das value*10? Value wird (abgesehen von der Initialisierung) nie irgendwo berechnet oder mit einem Wert belegt, somit müsste das doch heißen 0*10 (und damit sinnlos sein)?! Lösche ich es allerdings weg, stimmt wieder die Ausgabe nicht mehr, also für irgendwas muss es gut sein, nur wozu genau?

5. Rotes Doppelsternchen **: Was genau wird denn überhaupt in dieser Zeile berechnet? Da wird einfach in einer int Variable das int Ergebnis von isDigit gespeichert, oder?! Und wo genau findet jetzt eigentlich wirklich die Umwandlung von char nach int statt? In convert(), oder in isDigit(). Der Funktionsname lässt vermuten in convert(), ich tippe aber eher auf isDigit(). Habe ich recht?
-------------------

Sobald mir jemand die Angabe erklärt hat, würde ich gerne mit eurer Hilfe eine eigene Lösung für das Beispiel programmieren. Was ich bis jetzt rausfinden konnte, ist, dass es scheinbar irgendwie darum geht, die Bibliotheksfunktion atoi selbst zu programmieren.
Was sich meinem Verständnis aber noch entzieht, ist folgendes:
- Warum genau geht "012431243" als char string durch, bzw. warum gehen auch all die andernen Zahlenkombinatione als char strings durch? Ich weiß, dass hinter jedem Buchstaben, eine Zahl steckt, ASCII sei Dank, aber im Bereich von 0 bis 5 sind ja keine Buchstaben, sondern irgendwelche Sonderzeichen oder textspezifischen Sachen?!
- Warum fällt bei "13a" das a weg? a ist doch 97 als Zahl und das könnte ich doch locker in einem int speichern?!
- Wieso wird a56 zu 0? Hier wieder: 97 und 56 müsste doch locker in int passen?
- Wieso fällt beim letzten -56a weg, wenn man doch bei -124 sieht, dass auch die negativen Zahlen in int gespeichert werden können?

^^ Ich verstehe da einfach die Logik dahinter nicht ...
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

1. Ja, hier wird wirklich für jedes Zeichen einzeln geprüft. Die Funktion prüft also, ob EIN Zeichen eine Zahl darstellt oder nicht.

2. Wenn ich mich recht entsinne, werden alle Werte != 0 als TRUE interpretiert (gibt es ja in C nicht als eigenen Typ). So ohne es auszuprobieren, hätte ich daher auch auf 0-Terminierung getippt. Aber du sagst ja, das war's nicht... müsste ich erst mal ausprobieren ^^

3. Hier wird der Zeiger hochgezählt, ein *digits++ würde den Wert an der aktuellen Zeigerposition hochzählen (du kämst im Array nicht vorran). Und nebenbei müsste das von Dragonix angesprochene Problem auch greifen (Verändern eines String-Literals soll wohl böse sein ^^)

4. value = value * 10 + digit -> value erhält jedesmal einen neuen Wert, der erste (nach 0) ist value = digit. Nehmen wir mal die Zahl 133:

1) value = 0 * 10 + 1; (= 1)
2) value = 1 * 10 + 3; (= 13)
3) value = 13 * 10 + 3; (= 133)

Zu deinen anderen Fragen:
Die Umwandlung Zeichen -> Zahl geschieht in isDigit. Dabei werden die ASCII Werte gar nicht verwendet, sondern der Array-Index aus der for-Schleife liefert die Zahl. Durch diese Operation unter 4. wird dann die endgültige Zahl gebildet.

Das a wird ignoriert, da es nicht im char-Array vorkommt (for bei IsDigit läuft bis zum Ende durch -> return -1 -> break in der convert while-Schleife).

Hier wird ja nichts in Int gewandelt. Das erste Zeichen ist keine Zahl, also breche ab (Aufgabenstellung).

Das -56a fällt weg, da "+13-56a" keine gültige Zahl ist. Oder hast du schon mal ne Zahl gesehen, wo mittendrin ein Minus steht? ;) (gut "56a" ist eigentlich auch keine gültige Zahl, aber die Aufgabenstellung lässt das zu; sind halt nur Definitionen). An sich ist die Sache ganz einfach


Wenn das stimmt und length die Nullterminierung beinhaltet, dann muss aber schon length-1 richtig sein, v.a. da ich ja von 0 weg zähle?!
Length enthält die Anzahl der Zeichen ohne Nullterminierung. Du hast also length Zeichen, von str[0] bis str[length-1]. Was ich da meinte war, wenn du mit str[length] arbeitest, liegt du im besten Fall auf dem Null-Zeichen, im schlimmsten Fall bei einer Speicherschutzverletzung (man kann auch Strings ohne Nullterminator nutzen!)
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

gehn wirs mal durch:
die aufgabe verlangt im endeffekt ne funktion, die aus einem string das erste zahlenvorkommen rausfiltert. sprich: sobald der erste buchstabe auftaucht, soll abgebrochen werden.

wieso verstehst du die logik dabei nich? >< bei 13a zum bsp verwertet er die 13 und das a fällt weg. das a ist ja kein buchstabe mehr. hmm, ich glaub ich weis, was du falsch verstehst. was is denn ein char und was ist ein int? ^^ beides sind variablen typen und jeder typ hat ne andere interpretation des speicherwerts, wenn mans mal so will. char 65 ist A und int 65 ist ganz simpel die 65. bei char wird ne zahl eben als ascii code betrachtet. hmm, also wenn du dieses 13a hast, dann willst du 13 raushaben, und nicht ne summe der scii werte oder so. als char sieht das dann halt so aus: [49][51][97]. und er will aus diesem "kuddelmuddel" eben die zahl extrahiert haben.

gut, weiter...
isDigit prüft ganz simpel (wies der name sagt), ob der aktuell betrachtete char des char-arrays eine zahl ist oder nicht. wenn er das gleich fürs ganze array macht, wärs ja die hauptfunktion an sich - quasi ^^ die lösung find ich allerdings irgendwie umständlich. ein einfaches
Code:
bool isDigit(char s) {
    if((int)s > 47 && (int)s < 58) return true;
    return false;
}
hätts mMn auch getan >< naja, er wollt vllt protzen. die forschleife kenn ich so nämlich auch ned, kanns mir vllt dunkel vorstellen was er da treibt. aber wie gesagt: unnötig kompliziert *find*

gut, convert(): joa, er ignoriert erstma alle leerteichen und tabulatoren und checkt dann auf nen minuszeichen. dann muss ne zahl folgen, oder es bricht ab.




Code:
#include <stdio.h>
int convert(char s[]) {
    int value=0;
    bool negative = false;
 
    /* Leerzeichen */
    while (*s==' ' || *s=='\t') s++;
 
    /* Vorzeichen */
    if (*s=='-') {
        negative=true;
        s++;
    } else if (*s=='+') s++; // find ich persönlich jetz unnötig, da normalerweise keiner nen + schreibt ^^ isch wär da knallhard jong! abbruch, ABBRUCH! xD
 
    while (*s != '\0') {                
        if (!isDigit(*s)) break; // ich geh hier von meiner version der isDigit funke aus

        value = value*10 + (int)*s - 48;
        s++;
    }
         
    if (negative) return -value;
    return value;
}
wegen dem value: schau dir das ding doch mal genau an - die variable steht innerhalb der while schleife. gehs einfach mal im kopf durch, was da passiert:
im endeffekt lässt er alle leerzeichen bla links liegen und fängt danach mit der betrachtung an. isses ne zahl, wird weitergemacht, isses keine, abgebrochen. es können also am anfang beliebig viele leerzeichen oder tabs auftreten gefolgt von maximal einem vorzeichen und dann gehts los. er betrachtet dann den rest des strings solange, bis keine zahl mehr auftaucht (das break nach dem if-check) - oder das ende erreicht is (die while-bedingung).

value startet mit 0. das erste mal bei 13a rechnet er also value = 0(unser startwert)*10+1; => 1. jetzt ist value also 1. beim zweiten durchgang findet er die 3 - value = 1(im ersten durchgang errechnet)*10+3; => 13 ;) nun folgt ein a - abbruch. bei " a56" skippt er das leerzeichen und findet nun ein a - abbruch. value wird mit seinem initialwert von 0 zurückgegeben. " +13-56a" -> wieder werden die leerzeichen geskipped, das vorzeichen gelesen, die 13 rausgesucht/errechnet und dann das '-' gefunden - abbruch.

also wie gesagt: es geht NICHT darum, die ascii werte zu addieren, sondern aus nem string ne zahl zu extrahieren ;)
 
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

OK, vielen Dank euch beiden!

char assoziiere ich halt immer sofort mit Buchstaben bzw. Wörtern und wenn ich da dann "umwandeln in int" lese, ist mein erster Gedanke --> ASCII ... ;)
^^ Das war der Logikfehler. Wäre ja auch zuviel verlangt gewesen vom Lehrer, einfach in die Angabe zu schreiben: Filtert aus dem char string gültige Zahlen, wenn vorhanden, raus. :ugly: :schief:

@ DarkMo
Die Lsg oben stammt vom Lehrer, und ja, der Typ gibt gerne mal an mit dem, was er kann. :D
Ich persönlich finde ja schon das Arbeiten mit ausschleißlich Zeigern ohne Hilfsvariable etwas verwirrend. Ich hätte es immer so gemacht: *(s+i) ... i++ und nicht einfach dann den Zeiger selbst weitersetzen s++. Ich versteh's zwar, finde es aber für die Logik umständlicher, auch wenn's kürzer ist.

Den Bereich von 48 bis 57 zu prüfen finde ich echt schlau, BTW! Auf die Idee wäre ich jetzt nicht mehr gekommen, wo ihr mir den ASCII-Bezug schon ausgeredet habt. Aber so kann man sich natürlich trotzdem auf die Tabelle beziehen. :daumen:

@ bingo88
In der for-Schleife wird doch auf die Nullterminierung geprüft, hab's eben noch einmal ausprobiert und jetzt ging's. Wahrscheinlich habe ich beim Herumprobieren unabsichtlich wo einen kleinen Fehler reingebracht und deswegen ging's zuerst nicht.

Bei dem value habe ich mich von der Schreibweise verwirren lassen. Ich schreibe nämlich nie variable = variable +/*whatever, sondern immer += oder -= oder *= oder /=. Da hätte ich es sofort gesehen. Jetzt ist es eh total logisch, dass value in jedem Schleifendurchlauf einen anderen Ausgangswert hat.

Mir bleibt vorerst nur noch eine Frage: Ich hätte ja genauso gut z.B. a56 als int speichern können, oder? Wurden in der Angabe deswegen explizit char Felder verwendet, weil bei int sonst sofort der ASCII-Wert (hier z.B. für a) erkannt werden würde und somit alles als gültige Zahl durchgehen würde?

Werde auf jeden Fall mal probieren, das Ganze eigenständig zu programmieren. Thanks, again!
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

So, habe mal eine eigene Version geschrieben, allerdings kommt die noch nicht mit vorangestellten Buchstaben und eingeschobenen Rechenzeichen zurecht. Alles andere passt schon.
Code:
#include <stdio.h>
#include <stdlib.h>

int convert(char s[]) {
    int i=0, j=0, minus=0, result=0;

    [COLOR=seagreen]/* Leerzeichen/Tabulatoren entfernen und Vorzeichen prüfen */    while (*(s+i) != '\0') {
        if (*(s+j)==' ' || *(s+j)=='\t') {
            j++;
        } else if (*(s+i)=='+') {
            break;
        } else if (*(s+i)=='-') {
            minus=1;
            break;
        }
        i++;
    }

    [COLOR=seagreen]/* Prüfen auf Gültigkeit der Zahl und Rückrechnun von Dec nach Chr */    for (j; *(s+j) != '\0'; j++) {
        if (*(s+j)>=49 && *(s+j)<=57) { [COLOR=seagreen]/* ASCII-Bereich der chars */            result=result*10+*(s+j)-48;
        }
    }

    [COLOR=seagreen]/* dem Vorzeichen entsprechende Ausgabe */    if (minus==1) {
        return -result;
    }

    return result;
}

int main(void) {
    char *t1 = "012431234";
    char *t2 = "  -124";
    char *t3 = " +562";
    char *t4 = "13a";
    char *t5 = " a56 ";
    char *t6 = "     +13-56a";

    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t1, convert(t1), atoi(t1));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t2, convert(t2), atoi(t2));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t3, convert(t3), atoi(t3));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t4, convert(t4), atoi(t4));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t5, convert(t5), atoi(t5));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t6, convert(t6), atoi(t6));

    return 0;
}
Ich hätte es ja noch so probiert, aber da kommt nur Blödsinn raus:
Code:
for (j; *(s+j) != '\0'; j++) {
        if (*(s+j)>=49 && *(s+j)<=57) { [COLOR=seagreen]/* ASCII-Bereich der chars */            result=result*10+*(s+j)-48;
    [COLOR=royalblue][B]    } else {
            result=0;
        }[/B]    }
Und ein weiteres Problem lässt sich auch schon absehen: Wenn der Code erstmal funktioniert, wird er genau für die 6 in der Angabe vorgegebenen char Felder funktionieren, aber nicht für alle möglichen anderen. Habe nämlich schon mal testweise ++562 eingegeben, was ja 0 als Ergebnis liefern sollte. Ausgabe aber trotzdem 562 ...

Wenn erst einmal alles funktioniert, will ich deshalb das ganze noch so umändern, dass man wirklich alles mögliche eingeben könnte und es immer das selbe liefert, wie die Bibliotheksfunktion atoi.
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

Vielleicht noch nicht so ganz sauber, aber so gehts:
Code:
   [COLOR=blue] [COLOR=navy]int startPos = j;    /* Prüfen auf Gültigkeit der Zahl und Rückrechnun von Dec nach Chr */
    for (j; *(s+j) != '\0'; j++) {
        if (*(s+j)>=[COLOR=darkred]48 && *(s+j)<=57) { /* ASCII-Bereich der chars */
            result=result*10+*(s+j)-48;
        }
[COLOR=blue]        else if([COLOR=deepskyblue]!(*(s+j) == '+' || *(s+j) == '-') || [COLOR=navy]startPos != j) {
            return result;
        }    }
Als erstes, rot markiert, ist es wichtig auch die '0' als Zahl zu sehen, damit im untereren Abbruchkriterium, blau markiert, nicht auch die Null als Abbruch zählt.

Das Abbruchkriterium ist ja folgendes:
- Wenn etwas mitendrin im String keine Zahl ist (Dunkelblau); (Dazu muss ich natürlich wissen, ob ich mittendrinn bin, brauche also die Startposition)
ODER
- Wenn vorne keine Zahl oder + / - steht (hellblau)

Wenn abgebrochen wird, dann gibt die Funktion ihr bisheriges Ergebnis zurück. So wird aus "32432a7" 32432

EDIT: hab was übersehen:
damit auch ein eventuelles Minus gewertet wird, darf nicht sofort das Ergebnis zurückgegeben werden, sondern nur die Schleife beendet werden:
Code:
[COLOR=blue] [COLOR=black]       else if(!(*(s+j) == '+' || *(s+j) == '-') || startPos != j) {
           [COLOR=blue] break;        }
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

Ok, vielen Dank!

So funktioniert's jetzt:
Code:
int convert(char s[]) {
    int i=0, j=0, minus=0, startPos=0, result=0;

    [COLOR=seagreen]/* Leerzeichen/Tabulatoren entfernen und Vorzeichen prüfen */    while (*(s+i) != '\0') {
        if (*(s+j)==' ' || *(s+j)=='\t') {
            j++;
        } else if (*(s+i)=='-') {
            minus=1;
            break;
        }
        i++;
    }

    [COLOR=seagreen]/* Prüfen auf Gültigkeit der Zahl und Rückrechnung von Dec nach Chr */    startPos=j;
    for (j; *(s+j) != '\0'; j++) {
        if (*(s+j)>=48 && *(s+j)<=57) { /* ASCII-Bereich der chars */
            result = result*10+*(s+j)-48;
        } else if (!(*(s+j)=='+' || *(s+j)=='-') || startPos!=j) {
            return result;
        }
    }

    [COLOR=seagreen]/* dem Vorzeichen entsprechende Rückgabe */    if (minus==1) {
        return -result;
    } else {
        return result;
    }
}

int main(void) {
    char *t1 = "012431234";
    char *t2 = "  -124";
    char *t3 = " +562";
    char *t4 = "13a";
    char *t5 = " a56 ";
    char *t6 = "     +13-56a";

    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t1, convert(t1), atoi(t1));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t2, convert(t2), atoi(t2));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t3, convert(t3), atoi(t3));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t4, convert(t4), atoi(t4));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t5, convert(t5), atoi(t5));
    printf ("The value entered is %s. convert() liefert %d. atoi liefert %d.\n", t6, convert(t6), atoi(t6));

    return 0;
}
Bei deinem Edit komme ich nicht ganz mit. Ich hab's ausprobiert, aber gerade mit break setzt er ein Minus, wo keines hin soll?! Ich denke, mit einem sofortigen return result(=0) stimmt das schon. :)
 
Zuletzt bearbeitet:
AW: [C] Hilfe beim Programmieren und Verständnis-Fragen zu Code-Beispielen ...

Naja, 'return result;' gibt ein falsches Ergebnis bei zB. ("-323c47")
Bei welcher Eingabe hat er bei break ein Minus gesetzt? Eigentlich solltes damit stimmen, aber vielleicht hab ich ja was übersehen.

Wichtig ist halt nur nicht return 0; zu machen, weil sonst zB "627einPaarZeichen33" natürlich auch 0 anstatt 627 zurückgibt

Code:
int convert(char s[]) {
    int i=0, j=0, minus=0, result=0;

    /* Leerzeichen/Tabulatoren entfernen und Vorzeichen prüfen */
    while (*(s+i) != '\0') {
        if (*(s+j)==' ' || *(s+j)=='\t') {
            j++;
        } else if (*(s+i)=='+') {
            break;
        } else if (*(s+i)=='-') {
            minus=1;
            break;
        }
        i++;
    }
    
    int startPos = j;
    /* Prüfen auf Gültigkeit der Zahl und Rückrechnun von Dec nach Chr */
    for (j; *(s+j) != '\0'; j++) {
        if (*(s+j)>=48 && *(s+j)<=57) { /* ASCII-Bereich der chars */
            result=result*10+*(s+j)-48;
        }
        else if(!(*(s+j) == '+' || *(s+j) == '-') || startPos != j) {
            break;
        }
    }

    /* dem Vorzeichen entsprechende Ausgabe */
    if (minus==1) {
        return -result;
    }

    return result;
}
 
Zurück