'char'- Werte mit 'int'- Werten multiplizieren.



  • Kennst du die ASCII-Tabelle (nicht alle Zeichen sind druckbar 😉 ?

    PS: srand(time(0)) solltest du nur einmalig am Anfang deines Programmes aufrufen, damit du nicht immer die gleiche Zufallszahl erhältst...



  • <ctime> fehlt.

    benimus schrieb:

    // ...
    
        cin >> Passwort;  // du willst wahrscheinlich in Eingabe einlesen?
        for (i = 0; i != 9; i++)
        {
            srand (time (0));
            {  // <<================================+
                  Zufallswert = rand() % 50;  //    |-- wofuer das?
            }  // <<================================+
            
            Passwort[i] = Eingabe[i] * Zufallswert;
        }
        
        cout << Passwort << endl;
        cin >> Zufallswert;  // weil?
    }
    

    Was, wenn der Benutzer nicht genau 9 Zeichen eingibt?
    Wie kommt die einen C-String abschließende '\0' nach Passwort ?



  • Danke, ich habe es jetzt verändert, habe mich zu sehr auf die Schleife konzentriert, und dabei die fehlerhafte Eingabe übersehen. Aber wie genau meinst du das mit der abschließenden '\0' nach "Passwort"? Ich weiß zwar, dass sie an das Ende eines char Arrays gehört, falls die maximale Länmge nicht erreicht wird, aber wo genau fehlt sie denn im Code?

    Neuer Quellcode:

    #include <ctime>
    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    
    using namespace std;
    
    int main()
    {
        char Eingabe[10];
        int Zufallswert, i;
        char Passwort[10];
    
        cout << "Geben Sie ein Passwort ein:" << endl;
        cin >> Eingabe;
        for (i = 0; i != 9; i++)
        {
            srand (time (0));        
            Zufallswert = rand() % 50;
            Passwort[i] = Eingabe[i] * Zufallswert;
        }
    
        cout << Passwort << endl;
        cin >> Zufallswert; // ist einfach da, damit sich das Programm nicht sofort schließt, sobald das Ende erreicht ist.
    }
    


  • for (i = 0; i != 9; i++)
        {
            srand (time (0)); // Jedes Mal initialisieren ? Das macht man genau einmal!
            Zufallswert = rand() % 50;
            Passwort[i] = Eingabe[i] * Zufallswert; // Wenn der Nutzer nur 1 Zeichen eingegeben hat greifst du auf nichtexistente Daten zu
        }
    

    Außerdem müssen C-Strings 0-Terminiert werden. D.h. du hast 10 chars Passwort, und wo ist das '\0' ?



  • Und wie genau mach ich das in diesem Fall am besten? in meinem Buch steht dazu folgendes, allerdings bin ich mir nicht ganz sicher, ob das in diesem Falle wirklich auf diese Weise anwendbar ist.

    Um auch Texte in einem Array ablegen zu können, die kürzer sind als Größe des Arrays, muss das Textende markiert werden. Dazu wird eine 0 abgelegt. Dabei handelt es sich nicht um eine Ziffer '0', sondern um ein Nullbyte, das als '\0' oder als Zahlenkonstante 0 dargestellt wird. Eine Stringkonstante impliziert immer als letztes Zeichen diese Abschluss-Null. Damit ist der leere String ("") nicht wirklich leer. Er enthält bereits ein Element, die 0. Im folgenden Beispiel wird ein C-String definiert und mit einer String-Konstanten initialisiert:

    char Vorname[6] = "Kai";
    


  • Wir sind hier in C++. Also bin ich für eine Lösung in C++:

    #include <ctime> 
    #include <iostream> 
    #include <vector>
    
    using namespace std; 
    
    int main() 
    {
        srand (time (0)); //Initilisierung der Zufallsfunktion am Anfang
        std::string Eingabe; 
    
        cout << "Geben Sie ein Passwort ein:" << endl; 
        cin >> Eingabe; //Eingabe in einen C++ String statt in ein C-Array
    
    	std::vector<char> PasswordArray(Eingabe.length()); //Vector (Array) der die selbe Länge wie unsere Eingabe hat
    
    	int Zufallswert; 
    	for (int i = 0; i < Eingabe.length(); i++) //Schleife nur so oft laufen lassen wie wir Zeichen haben, keine fixe Anzahl
        {
            Zufallswert = rand() % 50; 
            PasswordArray[i] = Eingabe[i] * Zufallswert; //Array füllen
        } 
    
    	std::string Passwort(PasswordArray.begin(), PasswordArray.end()); //Array in string umwandeln
        cout << Passwort << endl; //Ausgabe des Passworts
        cin >> Zufallswert; // ist einfach da, damit sich das Programm nicht sofort schließt, sobald das Ende erreicht ist. 
    }
    


  • DarkShadow44 schrieb:

    #include <ctime>
    #include <iostream>
    #include <vector>
    #include <cstdlib>  // fehlte (srand)
    #include <string>   // fehlte
     
    using namespace std;  // den ganzen namensraum ausschuetten??
     
    int main()
    {
        srand (time (0));  // conversion from 'time_t' to 'unsigned int', possible loss of data
        std::string Eingabe;
       
        cout << "Geben Sie ein Passwort ein:" << endl;  // du meinst: [...]ein:\n"; - nicht: << endl;
        cin >> Eingabe;
     
        std::vector<char> PasswordArray(Eingabe.length());  // warum nicht gleich length() + 1,
    	                                                    // '\0' anhaengen und direkt ausgeben
    														// statt umweg spaeter ueber string?
    														// besser: warum nicht gleich einen string??
     
        int Zufallswert;  // warum nicht dort deklarieren, wo gebraucht?
        for (int i = 0; i < Eingabe.length(); i++)  // der Typ fuer Objektgroeszen ist std::size_t, nicht int
        {
            Zufallswert = rand() % 50;
            PasswordArray[i] = Eingabe[i] * Zufallswert;
        }
       
        std::string Passwort(PasswordArray.begin(), PasswordArray.end());
        cout << Passwort << endl;  // du willst: << '\n', nicht: << endl;
        cin >> Zufallswert;  // siehe http://www.c-plusplus.net/forum/111042-full
    }
    

    DarkShadow44 schrieb:

    Wir sind hier in C++. Also bin ich für eine Lösung in C++:

    Warum verwendest du dann time() , srand() und rand() aus der C-Stdlib? Warum nicht gleich auf Eingabe anstatt der umständlichen herumkopiererei?

    ➡

    #include <chrono>
    #include <random>
    #include <functional>
    #include <algorithm>
    #include <array>
    #include <string>
    #include <iostream>
    
    int main()
    {
    	std::array< int, std::mt19937::state_size > seeds;
    	std::random_device random_device;
    	std::generate_n( seeds.begin(), seeds.size(), std::ref( random_device ) );
    	std::seed_seq seed_sequence( seeds.begin(), seeds.end() );
    	std::mt19937 mt( seed_sequence );
    	std::uniform_int_distribution<> distribution( 1, 50 );
    
    	std::cout << "Geben Sie ein Passwort ein:\n";
    	std::string password;
    	std::cin >> password;
    
    	for( std::string::size_type i = 0; i < password.length(); ++i )
    		password[ i ] = password[ i ] * distribution( mt );
    
    	std::cout << "Passwort:\n" << password << "\n\n";
    }
    

    nee, eigentlich

    // ...
    	for( auto & i : password )
    		i = i * distribution( mt );
    // ...
    

    @benimus:

    Deine Probleme beginnen schon bei cin >> Eingabe; denn der istream& operator>>(istream &is, char *s) weiß nicht, wie groß der Speicherbereich ist, in den er Zeichen aus dem Stream schreiben soll:

    Extracts characters from is and stores them in s as a c-string, stopping as soon as either a whitespace character is encountered or (width()-1) characters have been extracted (if width is not zero) [<<== !!]. A null character ( charT() ) is automatically appended to the written sequence. The function then resets width to zero.

    Da width mit Lebensbeginn von std::cin gleich 0 ist, wird dieser operator>>() so lange Zeichen aus dem Stream extrahieren und in den Speicher beginnend bei *s schreiben, solange kein Whitespace (Space ' ' , Tab '\t' , Newline '\n' , ...) daherkommt. Gibt der Benutzer nun mehr Zeichen ein, als Platz in deinem Array ist, hast du einen klassischen Buffer overflow.
    Also musst du entweder vor dem Lesen mit operator>>() die Feldgröße des Eingabestreams auf die Länge des Arrays in das du lesen willst mit std::ios_base::width() oder std::setw() setzten oder du verwendest zum Einlesen std::istream::getline() , bei der du die Größe des zur Verfügung stehenden Speichers direkt als Parameter angeben kannst, also:

    #include <iostream>
    
    int main()
    {
    	char input[ 10 ];
    	std::cin.width( 10 );
    	std::cin >> input;
    	std::cout << '\"' << input << "\"\n";
    }
    

    oder

    #include <iostream>
    #include <iomanip>  // std::setw()
    
    int main()
    {
    	char input[ 10 ];
    	std::cin >> std::setw( 10 ) >> input;
    	std::cout << '\"' << input << "\"\n";
    }
    

    oder

    #include <iostream>
    
    int main()
    {
    	char input[ 10 ];
    	std::cin.getline( input, 10 );
    	std::cout << '\"' << input << "\"\n";
    }
    

    Bei getline() landen aber auch eventuell eingegebene Whitespace character in deinem Array. Ich weiß nicht, ob du das willst.

    Das nächste Problem ist die Bedingung deiner for-Schleife. Sie läuft immer stur von 0 bis inklusive 8 . Das geht nur gut, wenn der Benutzer weniger als 9 Zeichen eingibt. Ich mals dir auf:

    Beispiel "12345678"
    
    Eingabe     Passwort nach Schleife 0 bis 8
    [0]  '1'    [0]  '1'  * Zufallswert
    [1]  '2'    [1]  '2'  * Zufallswert
    [2]  '3'    [2]  '3'  * Zufallswert
    [3]  '4'    [3]  '4'  * Zufallswert
    [4]  '5'    [4]  '5'  * Zufallswert
    [5]  '6'    [5]  '6'  * Zufallswert
    [6]  '7'    [6]  '7'  * Zufallswert
    [7]  '8'    [7]  '8'  * Zufallswert
    [8]  '\0'   [8]  '\0' * Zufallswert  ==  0 * Zufallswert  ==  '\0'
    [9]  müll   [9] müll, der vor der Schleife schon dort war
    
    Beispiel "123456789"
    
    Eingabe     Passwort nach Schleife 0 bis 8
    [0]  '1'    [0]  '1' * Zufallswert
    [1]  '2'    [1]  '2' * Zufallswert
    [2]  '3'    [2]  '3' * Zufallswert
    [3]  '4'    [3]  '4' * Zufallswert
    [4]  '5'    [4]  '5' * Zufallswert
    [5]  '6'    [5]  '6' * Zufallswert
    [6]  '7'    [6]  '7' * Zufallswert
    [7]  '8'    [7]  '8' * Zufallswert
    [8]  '9'    [8]  '9' * Zufallswert
    [9]  '\0'   [9] müll, der vor der Schleife schon dort war
    

    Bei der Eingabe von "12345678" (und kürzeren Eingaben) wird also praktisch durch Passwort[8] = Eingabe[8] * Zufallswert ( = '\0' * Zufallswert = 0 * Zufallswert = 0 * egalwas = '\0' ) sichergestellt, daß die für Passwort errechneten Zeichen mit einem '\0' enden. Bei "123456789" hingegen wird die 0 in Eingabe[9] in der Schleife nicht bearbeitet und in Passwort[9] steht dasselbe wie vor der Schleife - Das kann, da das Array uninitialisiert ist, sonstwas sein und die chancen für '\0' stehen nicht gut ;).

    Deine Schleife sollte also entweder

    • über das gesamte Array laufen
      wobei ab i > strlen(Eingabe) auch Werte verarbeitet werden, die der Benutzer nicht eingegeben hat, also sinnlos gearbeitet wird,
    • solange i <= strlen(Eingabe)
      wobei die Abschließende '\0' verrechnet wird, oder
    • solange i < strlen(Eingabe)
      wobei Passwort vorher zB. durch Initialisierung bereits mit Nullen gefüllt ist ( char Eingabe[10] = {}; ) oder die Abschließende '\0' nach der Schleife gesetzt werden muss ( Passwort[strlen(Eingabe)] = '\0'; )

    Weitere Unschönheiten:

    • Initialisierung des Zufallsgenerators mit srand()
      wurde zwar schon genannt, aber der Vollständigkeit halber: Da du den Zufallsgenerator in der Schleife immer wieder mit der Systemzeit (die sich zwischen den Schleifendurchläufen sehr unwahrscheinlich ändern wird) initialisierst, bekommt Zufallswert sehr wahrscheinlich immer den selben Wert zugewiesen. Initialisiere also den Zufallsgenerator nur einmal am Programmstart.
    • Du deklarierst Variablen nicht dort, wo sie gebraucht werden. Variablen sollte man immer so lokal wie möglich halten.
    • Für Objektgrößen verwendet man std::size_t aus <cstddef> , nicht int
      denn für size_t ist garantiert, daß der Wertebereich für jedes mögliche Objekt eines C++-Programms ausreicht. Für int gilt das nicht.
    • Du verwendest Magic numbers.
      Bei jeder Änderung an den Arraydimensionen musst du in deiner jetzigen Version zwei Größenangaben und die Laufbedingung der for-Schleife ändern. Hättest du dafür eine Konstante, müsstest du nur eine einzige Stelle ändern, was weniger fehleranfällig ist.
      Bei der Berechnung von Zufallswert verwendest du die Konstante 50 . Hast du dir bei der Auswahl dieses Wertes irgendetwas spezielles überlegt? Hätte der Wert einen Namen, so könnte man seine Herkunft eventuell nachvollziehen. So muss irgendwo dokumentiert werden, weshalb er gewählt wurde.
    • Du verwendest std::endl wo ein Newline ( '\n' ) reicht
      std::endl schreibt nicht nur ein Newline in den Stream, sondern führt zusätzlich auch noch einen flush. Für weiteres siehe dort.
    • Du schüttest mit using den gesamten Namensraum std aus
      wodurch es schonmal zu Namenskonflikten kommen kann. Noch dazu ist in einem solch kleinen Programm das Mehr an Schreibaufwand durch die explizite Angabe des Namespace nicht der Rede wert.

    Auf deinen letzten Code übertragen:

    #include <limits>
    #include <cstddef>
    #include <ctime>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <iomanip>
    
    int main()
    {
    	std::size_t constexpr array_size = 10;
    	std::srand( static_cast< unsigned >( std::time( nullptr ) ) );
    
    	std::cout << "Geben Sie ein maximal " << array_size - 1 << " Zeichen langes Passwort ein:\n";
    	char input[ array_size ];
    	std::cin >> std::setw( array_size ) >> input;
    
    	char output[ array_size ] = {};
    	for( int i = 0; i < strlen( input ); ++i )
    		output[ i ] = input[ i ] * ( rand() % 50 );
    
    	std::cout << output << '\n';    
    
    	std::cin.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
    }
    

    Darüber, daß davon aber sehr, sehr unhandliche Passwörter ausgespuckt werden, sag' ich mal nichts.



  • Wenn du schon einen C++-RNG vorschlaegst, dann initialisier ihn auch wenigstens ordentlich mit std::random_device und std::seed_seq.



  • Jawohl!



  • benimus schrieb:

    Man gibt eine ein Wort, zahlen oder eine Kombination aus beidem ein dessen Ziffern dann mit einem zufälligem Wert addiert werden.

    Und jetzt liest Du diesen Satz mal laut vor. Wenn Dir nichts auffällt, dann lies ihn jemand anderen vor. Das nächste mal machst Du das bevor Du auf "Absenden" drückst. Das ist ja sonst eine Zumutung, das zu lesen.



  • Swordfish schrieb:

    nee, eigentlich

    // ...
    	for( auto & i : password )
    		i = i * distribution( mt );
    // ...
    

    Warum nicht gleich:

    // ...
    	std::transform(std::begin(password), std::end(password), std::begin(password), [&, gen=[&](){return distribution(mt);}](decltype(password)::value_type c){return c * gen();});
    // ...
    

    Oder um sich das doppelte begin zu sparen besser gleich:

    // ...
    	([&](auto b, auto e, auto f){std::transform(b,e,b,f);})(std::begin(password), std::end(password), [&, gen=[&](){return distribution(mt);}](decltype(password)::value_type c){return c * gen();});
    // ...
    

    Swordfish schrieb:

    std::array< int, std::mt19937::state_size > seeds;
    

    Das ist nicht generisch genug, besser das hier draus machen:

    std::array< std::random_device::result_type, std::mt19937::state_size > seeds;
    

    🙄



  • Swordfish schrieb:

    Warum verwendest du dann time() , srand() und rand() aus der C-Stdlib? Warum nicht gleich auf Eingabe anstatt der umständlichen herumkopiererei?

    Ich hab der Einfachheit halber das meiste 1:1 übernommen und den Code nur so umgeschrieben dass das Programm das macht was es soll.
    Der Rest ist nicht zwangläufig falsch, nur kein guter Stil, aber das ist eine andere Baustelle.

    //Gängige Praxis so. Der Verlust macht nichts, eventuell sollte man die Warnung noch unterdrücken
        srand (time (0));
    
        /* ... */
    
        //Man sollte die Änderung direkt am Passwort machen ja, hab vergessen dass C++ strings mutable sind
        std::vector<char> PasswordArray(Eingabe.length()); 
    
        /* ... */
    
        //Mal von der Warnung abgesehen reicht int vollkommen aus. 
        //Wenn dein Passwort so lang ist dass es nicht mehr in den Wertebereich von int passt hast du eh andere Probleme
        for (int i = 0; i < Eingabe.length(); i++)
        /* ... */
    }
    

    Im übrigen würde ich immer noch dazu raten einfach ein Zufallspasswort zu generieren.
    Da braucht du kein Usereingabe und kannst sicherstellen dass das passwort nur aus gewollten Zeichen besteht. 😉



  • Trollfish schrieb:

    Swordfish schrieb:

    nee, eigentlich

    // ...
    	for( auto & i : password )
    		i = i * distribution( mt );
    // ...
    

    Warum nicht gleich:

    // ...
    	std::transform(std::begin(password), std::end(password), std::begin(password), [&, gen=[&](){return distribution(mt);}](decltype(password)::value_type c){return c * gen();});
    // ...
    

    Um sich das doppelte begin() zu sparen. 🙄

    Trollfish schrieb:

    Swordfish schrieb:

    std::array< int, std::mt19937::state_size > seeds;
    

    Das ist nicht generisch genug, besser das hier draus machen:

    std::array< std::random_device::result_type, std::mt19937::state_size > seeds;
    

    🙄

    Da hast du natürlich Recht. 🙄

    DarkShadow44 schrieb:

    Im übrigen würde ich immer noch dazu raten einfach ein Zufallspasswort zu generieren.

    👍 🙂



  • Swordfish schrieb:

    [...]

    Mein Post war selbstverständlich sarkastisch weil mich deine Verschlimmbesserung dazu verleitet hat. Jemand der Integer Promotion nicht kennt wird deinen Code erst recht nicht verstehen. DarkShadow44s Lösung ist besser weil verständlicher.



  • Das ist mir schon klar, troll, aber meine Philosophie dazu ist vielmehr "Friss (=lerne!) oder stirb!".



  • Swordfish schrieb:

    Das ist mir schon klar, troll, aber meine Philosophie dazu ist vielmehr "Friss (=lerne!) oder stirb!".

    Also als mich mein Papi ins Wasser warf, stand ich Todesängste aus. Mehr ist nicht passiert. Kein Vertrauen zu dem Arschloch mehr, auch nie wieder gefunden. Schwimmen habe ich davon nicht gelernt. Kennt einer von dem Jungen Gemüse Todesangst?

    Es mag Leute geben, die eh schon im Prinzip schwimmen konnten, was deren Papi schon gesehen hat und ganz genau analysiert hat, und Papi hat da sinnvoll abgekürzt.

    Wenn Du den Fragesteller genug analysieren konntest, daß "Friss (=lerne!) oder stirb!" passt, dann kriegste einen *daumenhoch* 👍
    Wenn Du aus den Daten wähnen kannst, daß es passt, dann auch.

    Wenn Du aus "Philosophie" so berätst, weil er einfach in Dein Bild zu passen hat, dann schweig lieber.

    edit: Das war schon ein ganz schöner Klopper, dem Du da gekackt hast. Hast ja recht wie meistens. Bißchen kuscheliger geht auch. Dem Opfa nutzt die Bretthärte oft wenig. Dann kommt er immer wieda. Gib ihm, was er verdauen kann und alle vier Woche einen Brocken und er nerft nicht und wir sind ihn nach 3-4 Fragen als Frager los, weil er dann langsam, Antworter wird, statt vor einer unerklimmbaren Wand zu stehen.

    edit2: Ja, ich müßte mich auch mehr zurückhalten. Voller Begeisterung, aus 55 Jahren Programmiererfahrung den neuen Leuten was zeigen zu können, wenn sie mir ach bloß ein wenig zuhören würden, könnte ich ihnen Jahre des Lernens abkürzen… Geht aber nicht, Fehler muss man selber machen. Arcoth wäre jetzt 4-5 Jahre weiter zum Beispiel.



  • volkard, ich hab' Dich auch lieb! 😉

    Wissenserwerb ist in meinen Augen eine eine Holschuld. Sollte jemand nicht verstehen, was man einem gibt, kann er immer noch nachfragen ...


Anmelden zum Antworten