Anfänger ist verwirrt.. bitte um Aufklärung.



  • Hi,
    da ich neu bin, stelle ich mich erstmal vor:
    Mein Name ist Hendrik, ich bin 20 Jahre alt und versuche nun seit knapp zwei Wochen mir die Programmiersprache C++ beizubringen. Ich habe ansonsten keine Programmiererfahrung.

    Nun bräuchte ich allerdings Hilfe, denn ich blicke bei den folgenden Problemen nicht wirklich durch.

    Also Aufgabe ist es eigentlich nur, eine Zeichenkette des Typ string (welche nur aus Ziffern besteht) als eine natürliche Zahl darzustellen.

    Mein Versuch hier:

    int main() 
    {
        string Zahl("984721563");
        long int z = 0;  // reicht es hier nicht auch einfach long z = 0 zu schreiben?
    
        for(int i = 0; i < 9; ++i)
        {
            z *= 10;
            z += Zahl[i];
        } 
        cout << z; 
    }
    

    Allerdings spuckt er mir dann einen völlig absurden Wert aus, der gleichbleibt, auch wenn ich den in Inhalt des Strings verändere..

    Hier ist die Musterlösung:

    int main() 
    {
        string Zahl = "984721563";    // warum wird hier anders initialisiert? spielt das irgendeine Rolle?
        long int z = 0;  
    
        for(unsigned int i = 0; i < Zahl.size() ; ++i)
    // warum "unsigned int"? der Zähler kann doch ohnehin nicht in den Minusbereich gelangen..?
        {
            z *= 10;
            z += (int)Zahl.at(i) - (int)'0'; // warum überhaupt das "(int)" plötzlich..=
        } 
        cout << z; 
    }
    

    Warum wird dort "(int)Zahl.at(i)" verwandt? Macht das einen Unterschied?
    und man könnte ja meinen wenn man den Abschnitt "- (int)'0'" weglässt, würde der Wert "9847215630" rauskommen..stattdessen kommt dann die selbe absurde Zahl, wie bei meinem Versuch. Und wenn ich diesen Abschnitt an mein Programm dranhefte, dann spuckt er nur eine "0" aus..
    Ich bin verwirrt..

    ich hoffe, dass sich jemand erbarmt und mir weiterhilft 😃

    EDIT:
    Ich habe gerade mein Programm etwas umgeändert und
    " z += (int)ziff[i] - (int)'0'; "
    geschrieben. Und nun funktionierts! 😃 allerdings weiß ich nicht wirklich warum
    bzw. ich kann es nur erahnen.. also sind meine Fragen noch offen.


  • Mod

    Die beiden (int) sind unnötig und zudem noch schlechter Stil, weil es in C++ sicherere Casts gibt. Mit Zeichen kannst du auch ohne Casts ganz normal rechnen und ich ahne schlimmes über den Lehrer, wenn dieser das nicht wusste.

    Was du noch wissen musst beim Rechnen mit Zahlen ist, dass der Wert des Zeichens '0' nicht 0 ist, sondern in der Regel (d.h. bei ASCII Zeichencodierung) ist er 48. Das ist aber nicht schlimm, denn es ist dir garantiert, dass die Ziffern '0' bis '9' im Zeichensatz hintereinander stehen, das heißt wenn du vom Zeichen '0' das Zeichen '0' abziehst bekommst du auch den Wert 0, wenn du '0' von '1' abziehst bekommst du 1 und so weiter. Jetzt sollte klar sein, was die Musterlösung tut und wieso bei deiner Lösung der falsche Wert herauskommt.

    Noch die Einzelfragen:

    long int z = 0;  // reicht es hier nicht auch einfach long z = 0 zu schreiben?
    

    Ja. int und long int sind der gleiche Typ. Ebenso ist unsigned int gleich dem unsigned ohne was dran, short int ist short und so weiter.

    string Zahl = "984721563";    // warum wird hier anders initialisiert? spielt das irgendeine Rolle?
    

    Nein, das ist beides das gleiche. Naiv würde man denken, dass beim zweiten noch ein Kopierkonstrukutor aufgerufen wird, der aber dank RVO komplett wegoptimiert wird. Die beiden Arten der Initialisierung ist daher identisch, es ist bloß Geschmackssache.

    // warum "unsigned int"? der Zähler kann doch ohnehin nicht in den Minusbereich gelangen..?
    

    Eben drum. Wenn man kein Vorzeichen braucht, wieso dann int?

    z += (int)Zahl.at(i) - (int)'0'; // warum überhaupt das "(int)" plötzlich..=
    

    Ahnungsloser Tutor, siehe oben.

    Außerdem hier noch die Lösung, falls man normale Aufgaben bekäme, die nicht die Nachprogrammierung der Standardbibliothek als Ziel hätten:

    int main()
    {
        string Zahl("984721563");
        long int z;
        stringstream parser(Zahl);  // Aus dem Standardheader sstream
        parser >> z;
        cout << z;
    }
    


  • Sasori schrieb:

    Warum wird dort "(int)Zahl.at(i)" verwandt? Macht das einen Unterschied?

    .at() ist im Gegensatz zu [] "checked", wirft also kontrolliert eine Exception statt in "undefined behaviour" zu enden wenn der Index "out of range" ist.
    Was in dem Beispiel aber vollkommen egal ist.
    Der "(int)" Cast ist auch vollkommen unnötig, da es bloss die Standard-Conversion ist die hier nochmal explizit hingeschrieben wird.
    Wer sowas schreibt hat entweder nen fiesen Humor, oder (wahrscheinlicher) nicht viel Ahnung von C++.

    und man könnte ja meinen wenn man den Abschnitt "- (int)'0'" weglässt, würde der Wert "9847215630" rauskommen

    Nein, könnte man nicht meinen.
    Das - '0' ist essenziell. Sonst rechnet er nämlich an Stelle von 0 ... 9 mit den ASCII Werten der Zeichen '0' ... '9'.

    BTW: ich würde das so schreiben:

    #include <string>
    #include <iostream>
    
    int main() 
    {
        std::string zahl = "984721563"; // "einfach so" weil ich es eben so schreibe, man könnte aber genau so gut mit () initialisieren
        long z = 0; // ja, int kann man weglassen, und lasse ich auch weg
    
        for (std::size_t i = 0; i < zahl.size(); i++) // std::size_t weil std::size_t für genau sowas gemacht wurde
        {
            z *= 10;
            z += zahl[i] - '0'; // weil es keinen Sinn macht hier blöd explizit rumzucasten
        } 
    
        std::cout << z << std::endl; // mit Zeilenumbruch abschliessen wäre schon schön
    }
    


  • SeppJ schrieb:

    Außerdem hier noch die Lösung, falls man normale Aufgaben bekäme, die nicht die Nachprogrammierung der Standardbibliothek als Ziel hätten:

    int main()
    {
        string Zahl("984721563");
        long int z;
        stringstream parser(Zahl);  // Aus dem Standardheader sstream
        parser >> z;
        cout << z;
    }
    

    Naja, dann lieber gleich

    int main()
    {
        puts("984721563");
    }
    

    🤡



  • std::string::size_type



  • [Edit]: hab ich wirklich so lange gebraucht die Antowrt zu tippen !?!?!?!?

    Als erstes mag ich dir mal ein Buch ans Herz legen,

    http://www.amazon.de/C-Primer-Plus-Developers-Library/dp/0321776402/ref=sr_1_cc_1?s=aps&ie=UTF8&qid=1331723226&sr=1-1-catcorr

    es kann auch online gelesen werden, hier ...

    http://books.google.de/books?id=eC-iLe_iET8C&printsec=frontcover&hl=de&source=gbs_ge_summary_r&cad=0#v=onepage&q&f=false

    Nun mal zu deinem Code:

    long int z = 0;  // reicht es hier nicht auch einfach long z = 0 zu schreiben?
    

    Im Grunde kann man auch einfach long schreiben. Ob es dann ein long int oder long double wird ergibt sich dann aus dem übergebenen Wert. So wie es da steht wird es einfach fix festgeschrieben das es ein integer Wert ist.

    string Zahl = "984721563";    // warum wird hier anders initialisiert? spielt das irgendeine Rolle?
    

    String ist grundsätzlich mal eine Klasse, ein Blick in die Header Datei kann da schon einiges aus dem Weg räumen, und warum anders initialisiert ist einfach. Du könntest anstatt string auch char c_string[] nehmen. Und char initialisiert man mit '<ein zeichen>' oder wenn es ein array aus chars ist mit einem string literal also so "<mehrere zeichen>".

    for(unsigned int i = 0; i < Zahl.size() ; ++i)
    // warum "unsigned int"? der Zähler kann doch ohnehin nicht in den Minusbereich gelangen..?
    

    Die Methode .size() der Klasse string gibt einen unsigned integer zurück, deswegen schreibt man unsigned int um beim compilieren nicht eine Warnung zu erhalten das ein signed int mit einem unsignet int verglichen wird.

    z += (int)Zahl.at(i) - (int)'0'; // warum überhaupt das "(int)" plötzlich..=
    

    (int) ist ein typecast der dem Compieler sagt das der nachfolgende Wert anstatt als Zeichen als integer Wert zu interpretieren ist. Obwohl dieser Typecast ist eigendlich C und nicht C++. Schau dir mal dazu die Typecasts an auf http://www.cplusplus.com/.



  • Videonauth schrieb:

    Nun mal zu deinem Code:

    long int z = 0;  // reicht es hier nicht auch einfach long z = 0 zu schreiben?
    

    Im Grunde kann man auch einfach long schreiben. Ob es dann ein long int oder long double wird ergibt sich dann aus dem übergebenen Wert. So wie es da steht wird es einfach fix festgeschrieben das es ein integer Wert ist.

    NEIN, ein long ist immer ein long int ! Wenn du

    long i = 1.23;
    

    schreibst, dann ist i trotzdem ein long int , und hat den Wert 1. Und der Compiler wird vermutlich ne Warning produzieren wegen "truncation of numerical literal" oder dergleichen.



  • Ok selber wieder was gelernt. Danke für die Aufklärung.



  • Videonauth schrieb:

    Die Methode .size() der Klasse string gibt einen unsigned integer zurück

    Erläuterung: "unsigned integer" ist hier im allgemeinen Sinne ("vorzeichenlose Ganzzahl") zu verstehen. Es handelt sich nicht um den C++-Datentyp "unsigned int". Die Methode gibt einen Wert vom Typ size_t zurück. Das ist ein typedef, der von System zu System anders umgesetzt sein kann. Auf meinem System ist es z.B. ein unsigned long. Darauf verlassen kann man sich aber nicht, deshalb einfach size_t verwenden, dann gibts auch keine Probleme.



  • Danke für die Antworten,
    aber ein paar Fragen habe ich noch 😉
    Wie kommt es, dass er mir bei " string Zahl("984721563") "
    nicht direkt die Bedeutung der Ziffern erkennt, und stattdessen mit den Indexnummern der ASCII-Tabelle arbeitet?

    Und werden Symbole in der ASCII-Tabelle auch gleich richtig interpretiert (also das Symbol "1" als Mengenangabe usw.) oder müsste man das speziell deklarieren?
    (also sind die Symbole in der ASCII Tabelle nur die Repräsentionen ohne Semantik?)

    Und wenn das der Fall ist, könnte man dann nicht auch einfach
    " string Zahl<int>("984721563") "
    oder sowas in die Richtung schreiben, anstatt dann später die 'O' abzuziehen?

    ..
    Ist der Standarttyp von strings Char?
    Das würde erklären weshalb er auf die ASCII-Tabelle zugreift..
    aber ich dachte in char geht es nur um das Alphabet.
    ..............

    Achja, ich habe keinen Lehrer, da ich selbstständig lerne.
    Die Übung, sowie die Lösung, habe ich aus einem Lehrbuch dessen Name ich vermutlich nicht schreiben darf. (Wegen Werbung und so..)


  • Mod

    Sasori schrieb:

    Danke für die Antworten,
    aber ein paar Fragen habe ich noch 😉
    Wie kommt es, dass er mir bei " string Zahl("984721563") "
    nicht direkt die Bedeutung der Ziffern erkennt, und stattdessen
    mit den Indexnummern der ASCII-Tabelle arbeitet?

    Weil du eine Zeichenkette hast, keinen Zahlenwert.

    3 ist der Zahlenwert 3. Die Anzahl der folgenden o's: ooo
    '3' ist das Zeichen 3. Ein umgedrehtes E.
    "3" ist eine Zeichenkette. Diese hier besteht nur aus einem einzigen Zeichen, dem Zeichen '3'.

    Und werden Symbole in der ASCII-Tabelle auch gleich richtig interpretiert (also das Symbol "1" als Mengenangabe usw.) oder müsste man das speziell deklarieren?
    (also sind die Symbole in der ASCII Tabelle nur die Repräsentionen ohne Semantik?)

    Ich verstehe nicht, was du genau meinst, aber ich glaube die Antwort oben ist genauso gültig.

    Und wenn das der Fall ist, könnte man dann nicht auch einfach
    " string Zahl<int>("984721563") "
    oder sowas in die Richtung schreiben, anstatt dann später die 'O' abzuziehen?

    Technisch gesehen ja, so ähnlich könnte man das machen. Am einfachsten durch long int zahl = 984721563; 🙂 . Denn "984721563" ist, wie schon gesagt, eine Zeichenkette und kein Zahlenwert. Du verfehlst irgendwie den Sinn der Aufgabenstellung, die wohl den Zweck hat, dass du diesen Unterschied begreifst.

    ..
    Ist der Standarttyp von strings Char?

    Ja. std::string ist std::basic_string<char>

    Das würde erklären weshalb er auf die ASCII-Tabelle zugreift..
    aber ich dachte in char geht es nur um das Alphabet.

    Zeichen sind im Computer auch nur Zahlen.


Anmelden zum Antworten