Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Infinite loop problem C++ for char instead of int

  1. Jun 1, 2015 #1

    ChrisVer

    User Avatar
    Gold Member

    I need some help in optimizing a code with a prompt yes/no...My problem is that if I mistype within the loop for r some character (let's say I'm missing the number 8 on the keyboard for u) the program gets into an infinite loop.
    Do you know some way that I could solve this problem? Like printing "you gave a character and not an integer for r"...

    Code (C):

    #include<iostream>
    using namespace std;

    int main(){

      char ans;

       do{
          int r=1;
          while(r==1){
             cout<<"you said yes"<<endl<<endl;
             cout<<"are you sure?";
             cin>>r;
          }
          cout<<"Do you want to repeat? (y/n)";
          cin>>ans;
          if(ans=='n' || ans=='N') {cout<<"Thanks for working with me"<<endl;}

       } while(ans=='y' || ans=='Y');

       system("pause");
       return 0;
    }

     
     
    Last edited by a moderator: Jun 1, 2015
  2. jcsd
  3. Jun 1, 2015 #2
    I am not sure what you are trying to achieve.
    Why are you reminding the operator of this program that they chose to continue, and then expecting them to enter an integer value?
     
  4. Jun 1, 2015 #3

    ChrisVer

    User Avatar
    Gold Member

    I am sorry... that is a general sketch of a code I'm creating, and only aims at reproducing the problem I have (so it doesn't make much sense as a code by itself :woot:).
    The problem is that, if after "are you sure?" question, I type in a character-type element, I'm repeatedly getting "you said yes, are you sure?" (forever).
    What I'm asking is if there is a solution like telling it: -if I type a char instead of an integer, give me another chance to retry and avoid entering this eternal loop-.
     
  5. Jun 1, 2015 #4

    ChrisVer

    User Avatar
    Gold Member

    eg I am not sure if a "break" would work...since the mistake would be fatal - the program expects and integer for r, yet I give it a char (so the question would be whether I'm able to distinguish between those two or not)... plus the break wouldn't give me a second chance...
     
  6. Jun 1, 2015 #5

    ChrisVer

    User Avatar
    Gold Member

    The real program would look like this:

    Code (C):

    char ans;
    do{

    int w1=0;
    while(w1==0 || w1>2){
    cout<< "Would you like to calculate: "<<endl <<"1. integral" <<endl <<"2. derivative";
    cin>>w1;
    if(w1!=1 || w1!=2){cout<<"Please give a legit answer";}
    }

    if(w1==1) { //write code to solve the integral }
    if(w1==2) { //write code to solve the derivative }

    cout<<"Would you like to retry?(y/n)";
    cin>>ans;
    }while(ans=='y' || ans=='Y')

     
     
  7. Jun 1, 2015 #6
    Ah, so the integer is (either 1 or 2 in your description, but it could be more) is basically like a menu choice.
    Since menu choices can be be implemented in all kinds of ways, I think it would be best to get the user to enter a string.
    You can then validate what they entered any way you like and then call whatever other functions as necessary.
     
  8. Jun 1, 2015 #7
    w1!=1 || w1!=2 is always true.
     
  9. Jun 1, 2015 #8

    ChrisVer

    User Avatar
    Gold Member

    I tried passing it as a string... In particular like that:

    Code (C):

    string w1="a";
    while(w1 != "1" || w1!="2"){
    cout<<"Would you like to calculate: "<<endl <<"1. integral"<<endl <<"2. derivative";
    cin>>w1;
    if(w1!="1" || w1!="2"){cout<<"Please give a legit answer";}
    }
     
    But it doesn't even recognize my "1" or "2" inputs (I get the error message)...

    How is that so? I input the w1... It's always true everytime I try to rerun the calculation (at the end of do-while loop). It has to be at first true in order to enter in the while loop, but then I replace it manually.
    So
    step 1: at first it's true, I enter the while
    step 2: I choose a value for w1 within the while
    step 3: checks the new value I put in w1 position, and if it is not 1 or 2 it gives me the error.
    If there is no error step 4: goes back to the while, checks it , it's not fitting any more and I leave the while.
    If there is an error step 4: goes all the way into the while again.
     
  10. Jun 1, 2015 #9
    I gotta go soon, but your 'if' statement there needs to be looked at.
    You are saying that w1 must simultaneously be both "1" and "2" in order to be valid.
    Since that is impossible, your test of w1 will always be 'not legit'.

    Will see how it's going tomorrow maybe .
     
  11. Jun 1, 2015 #10

    ChrisVer

    User Avatar
    Gold Member

    I think the problem is in the 'while' statement (did some fast checks with a smaller program)...
    I think it somehow has problems in recognizing the || (=or) operator in this context:
    Code (C):

    while( w1 != "1" || w1 != "2" ){}
     
    I tried eg removing the rest statements after ||:
    Code (C):

    while( w1 != "1"){}
     
    and then it recognized my "1" input and opened the category 1.
    that's strange...


    as for the "if" it seems right... it's looking whether w1=1 or 2...if it's not 1 or if it's not 2 (that's the statement) it prints the error.

    Goodnight, thanks for so far...
     
    Last edited: Jun 1, 2015
  12. Jun 1, 2015 #11

    ChrisVer

    User Avatar
    Gold Member

    I fixed it- it needed && instead of || both in while and if....
    However I don't understand why....
     
  13. Jun 1, 2015 #12

    wle

    User Avatar

    The C++ FAQ gives an answer to this (second and third entries on the page):

    https://isocpp.org/wiki/faq/input-output#stream-input-failure

    Basically, you're supposed to explicitly check for an input error and, if necessary, clear/restore the input stream.

    (I can't resist: I found this through Yossi Kreinin's FQA, which gives a more opinionated answer.)
     
  14. Jun 1, 2015 #13

    Mark44

    Staff: Mentor

    DeMorgan's Laws, that's why
    What would it take to exit the while loop?
    Ans: if w1 == '1' or w1 == '2'

    So, to continue another iteration of the loop, the above would have to be false. That would be !(w1 == '1' || w1 == '2'). Here's where De Morgan comes in. If p and q are logical statements (statements whose truth value is either true or false, then !(p || q) is equivalent to !p && !q.

    In terms of your code, the negation of [w1 == '1' or w1 == '2'], that is, !(w1 == '1' || w1 == '2'), would be written as !(w1 == '1') && !(w1 == '2')
     
  15. Jun 1, 2015 #14

    Mark44

    Staff: Mentor

    Because a string is fundamentally different from a character. The name of a character variable evaluates to the character stored at the variable's memory location. I'm less familiar these days with C++ than C, but I believe that the name of a string variable evaluates to the address of the first byte in memory of the string. An expression like w1 != "1" would always evaluate to true, since the location of the memory for w1 would be different from the location of the string literal "1".


     
  16. Jun 1, 2015 #15

    ChrisVer

    User Avatar
    Gold Member

    I am trying to understand this logic now...
    It somewhat seems correct (and it certainly is, judging from the fact that the code works fine with it).
    Yet how would the program understand what I wrote?
    while( w1 != "1" || w1 !="2" )
    Because I am still reading it 'correctly' : while the string assigned to w1 is not 1 or w1 is not 2 (what is left to send you in the loop is 0,3,4,...,a,b,...) , then you enter the while... however when I assign to it w1=1 , I'm still into the loop.

    However I'm afraid of using the && in a while, because it generally can be tricky... for example for an integer K>=0, if I write:
    while( K==1 && K==2 )
    it can lead to a mistake (because K=1 and K=2 are not compatible and so the statement is always wrong). || should be used in this example.

    If I write:
    while( K!=1 && K!=2 )
    then am I getting what I would get by asking
    while(K!=1 || K!=2)
    and are the numbers K=0, 3,4,5...or not?

    The first enters the while if [itex]K \ne 1[/itex] and [itex]K \ne 2[/itex], that is the set [itex]K \in \{ 0,3,4,... \} [/itex]

    The second enters the while if [itex]K \ne 1[/itex] or [itex]K \ne 2 [/itex]. This sounds a little weird... because what will happen with K=1 or K=2?
    It depends on how someone reads the constraint. K=1, satisfies the [itex]K \ne 2[/itex] so it can enter from that door, although that would violate the [itex]K\ne 1[/itex]... I think it's because "or" in this case doesn't really rule out a condition.

    I think I understood it... it's the difference between the union and the joint.
     
  17. Jun 1, 2015 #16

    Mark44

    Staff: Mentor

    You might as well write
    while (1)
    {
    ...
    }
    As I explained in the previous post, in the expression w1 != "1", you are not comparing the characters in the two strings - you are comparing the addresses of the two strings, and these will be different.In C or C++ you should never do this sort of comparison with strings.

    No. These are different, and their behaviors will be different.
    In your first example above (with &&), the loop will start another iteration if K is any number other than 1 or 2.
    In the second example (with ||), you will have an infinite loop. The truth table shows the situtation here. The first column shows a few values of K. The second and third columns show the expression K != 1 and K != 2, respectively, and the fourth column shows the compound expression that is controlling your while loop.
    For two logical (Boolean) expressions ORed together, the overall expression is true if either expression is true or if both expressions are true.

    If K == 1, then K != 1 is false, while K != 2 is true (2nd line in truth table). Similarly, if K == 2, then K != 1 is true and K != 2 is false (3rd line).

    $$\begin{bmatrix} K & K != 1 & K != 2 & K!=1 || K!=2 \\
    1 & F & T & T \\
    2 & T & F & T \\
    3 & T & T & T \end{bmatrix}$$

    See above.
    Pretty much, although the usual term is "intersection" instead of "joint".
    An element x can belong to ##A \cup B## if ##x \in A## or ##x \in B##, which doesn't rule out x being in both sets (i.e., in ##A \cap B##). This is exactly the way that OR (||) works.

    An element x can belong to ##A \cap B## if and only if ##x \in A## and ##x \in B##, which is the same as how AND (&&) works.
     
  18. Jun 2, 2015 #17

    meBigGuy

    User Avatar
    Gold Member

    You are missing the whole point that several have already pointed out.

    No matter what number you pick it will EITHER be "not equal to 1" or "not equal to 2"
    If you pick 1, it is not equal to 2, so the loop executes.
    If you pick 2, it is not equal to 1, so the loop executes.
    etc

    You are correct, that you need &&. If the number is "not equal to 1" AND ALSO "Not equal to 2" then you want to execute the conditional.
     
  19. Jun 2, 2015 #18
    Use a switch

    Code (Text):
    switch(ans){
       case 'n':
       case 'N':
             //do something
             break;
       case 'y':
       case 'Y':
             //do something else
             break;
        default:
             //Not y or n, do something else
    }
     
  20. Jun 2, 2015 #19

    FactChecker

    User Avatar
    Science Advisor
    Gold Member

    In C, it is common to test input by calling sscanf with the format that matches the expected input. It returns a negative number if the input is not of the correct type. I'm sure that there is a C++ equivalent. If not, you can put C code in C++ programs.

    Code (Text):

    if( ! sscanf(  input_string, "%d", &r) ){  // attempt to read an integer from an input string to the integer variable r.
       // process error if the data in input_string is not an integer
    }
     
     
  21. Jun 3, 2015 #20

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    First, a side remark:
    The OP is using std::string. This is *not* a char*. C++ has operator overloading. The function bool operator==(const std::string& lhs, const char* rhs) compares the contents of the lhs and rhs arguments for equality.

    This is so very important.

    The C++ I/O functions do not throw an exception because of a bad parse. They instead set the stream's failbit. Setting the failbit is one of three ways a stream can be marked as "bad". Once a stream is marked as "bad" all subsequence extraction operations on the stream will do nothing. The code in the opening post went into an infinite loop for precisely this reason.

    One can fiddle around with clearing the bad bits, but that can be a pain, particularly when chaining extractions.

    When doing I/O in C or C++, one must always test for problems. A simple way of doing this in C++ is to test with an if. For example, if (std::cin) { do_something_with_cin(); } or if (! std::cin) { deal_with_problem_in_cin(); }.


    Personally, I avoid the stream extraction operator on user input. When asked to type a number, a user will enter "foo". Or the user might get frustrated and type control-D (which is end of file on my system). In the case of the code in post #5, I would have done something along the lines of
    Code (C):

    int action = 0;
    while (1) {
       std::cout << prompt;
       std::string response;
       std::getline (std::cin, response); // Note use of getline rather than stream extraction.
       if (! std::cin) {
          std::cerr << "EOF detected.\n";
          break;
       }
       else if (response == "1") {
          action = 1;
          break;
       }
       else if (response == "2") {
          action = 2;
          break;
       }
       std::cout << "Please give a legit answer ";
    }

    // At this point, action is
    //   1 (user eventually type a line containing just the character '1'),
    //   2 (user eventually type a line containing just the character '2'), or
    //   0 (user got fed up and typed control-D or disconnected the keyboard).
     
    About that std::getline: One reason is that std::cin is line buffered on many systems. You're not going to read something until the user has ended the input line with a return. Another reason is that this reading a full line works nicely with a prompt that (presumably) starts on a new line.

    About the break statements: People have been taught not to use that construct. There are times when breaking out of the middle of the loop is a good idea. This is one of those times. The alternative is to create dummy variables and complex boolean expressions. There are a lot of programmers who don't understand complex boolean expressions. Almost every programmer can understand breaking out of a loop on reading an EOF or on reading the desired type of response.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook




Similar Discussions: Infinite loop problem C++ for char instead of int
Loading...