Infinite loop problem C++ for char instead of int

  • C/++/#
  • Thread starter ChrisVer
  • Start date
  • #1
ChrisVer
Gold Member
3,352
452
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"...

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:

Answers and Replies

  • #2
3,379
944
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?
 
  • #3
ChrisVer
Gold Member
3,352
452
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-.
 
  • #4
ChrisVer
Gold Member
3,352
452
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...
 
  • #5
ChrisVer
Gold Member
3,352
452
The real program would look like this:

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')
 
  • #6
3,379
944
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.
 
  • Like
Likes ChrisVer
  • #7
362
26
w1!=1 || w1!=2 is always true.
 
  • #8
ChrisVer
Gold Member
3,352
452
I tried passing it as a string... In particular like that:

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)...

w1!=1 || w1!=2 is always true.
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.
 
  • #9
3,379
944
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 .
 
  • Like
Likes ChrisVer
  • #10
ChrisVer
Gold Member
3,352
452
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:
C:
while( w1 != "1" || w1 != "2" ){}
I tried eg removing the rest statements after ||:
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:
  • #11
ChrisVer
Gold Member
3,352
452
I fixed it- it needed && instead of || both in while and if....
However I don't understand why....
 
  • #12
wle
316
139
Do you know some way that I could solve this problem? Like printing "you gave a character and not an integer for r"...
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.)
 
  • Like
Likes ChrisVer
  • #13
34,156
5,777
I fixed it- it needed && instead of || both in while and if....
However I don't understand why....
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')
 
  • Like
Likes ChrisVer
  • #14
34,156
5,777
I tried passing it as a string... In particular like that:

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?
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".


ChrisVer said:
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.
 
  • #15
ChrisVer
Gold Member
3,352
452
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')
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.
 
  • #16
34,156
5,777
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.
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.

ChrisVer said:
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?
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}$$

ChrisVer said:
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?
See above.
ChrisVer said:
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.
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.
 
  • Like
Likes ChrisVer
  • #17
meBigGuy
Gold Member
2,323
405
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.
 
  • #18
1,518
618
Use a switch

Code:
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
}
 
  • Like
Likes ChrisVer
  • #19
FactChecker
Science Advisor
Gold Member
5,787
2,155
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:
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
}
 
  • #20
D H
Staff Emeritus
Science Advisor
Insights Author
15,393
685
First, a side remark:
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".
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.

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
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:
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.
 
  • Like
Likes ChrisVer
  • #21
ChrisVer
Gold Member
3,352
452
Use a switch
Thanks for the remark, I was also told by a friend I should try using the switch-case , and at the moment I'm trying to understand how it works (or in what way it is better than if or while). I think for many comparisons it is easier to read.
For example if I didn't put 1 or 2 as choices but let the choice be the sting "integral" or "derivative", then I would have to write many arguments inside my tests (like "integral" or "Integral" or "INTegral" etc...

The function bool operator==(const std::string& lhs, const char* rhs) compares the contents of the lhs and rhs arguments for equality
A question on that: is that function in the std::string ? Because I don't think I made use of it.

As for the code...
You define an integer "action" which enters you in the loop. I understand the getline() in this context and I have already replaced all the cins that have strings with it. I thought the difference were in that the std::cin will take as input anything you have written up to a spacebar or return.
I don't understand the cout<<prompt

if (! std::cin) {
std::cerr << "EOF detected.\n";
break;
I don't understand this check either... The syntax is similar though to the link @wle posted.
 
  • #22
D H
Staff Emeritus
Science Advisor
Insights Author
15,393
685
The function bool operator==(const std::string& lhs, const char* rhs) compares the contents of the lhs and rhs arguments for equality.
A question on that: is that function in the std::string ? Because I don't think I made use of it.
While you didn't the overload of operator==, you did use bool operator!=(const std::string& lhs, const char* rhs). In particular, you used it where you wrote w1 != "1".

ChrisVer said:
I thought the difference were in that the std::cin will take as input anything you have written up to a spacebar or return.
Suppose you are on a system in which terminal input is line buffered. A mess will ensue if you use std::cin >> response to read user input and the user enters foo bar baz<return>. Using std::getline combats that problem.

ChrisVer said:
I don't understand the cout<<prompt
Think of prompt as a string variable. I was being lazy and didn't bother to cut and paste your prompt.


ChrisVer said:
D H said:
C:
   if(! std::cin){
      std::cerr<<"EOF detected.\n";
     break;
   }
I don't understand this check either... The syntax is similar though to the link @wle posted.
The C++ I/O streams provide six boolean functions to test the status of a stream:
  • if (stream.good())tests whether the stream is "good" (all status bits off).
  • if (stream.fail())tests whether the stream is marked as "failed" (failbit set).
  • if (stream.bad())tests whether the stream is marked as "bad" (failbit or badbit set).
  • if (stream.eof())tests whether the stream is at EOF (eofbit set).
  • if (!stream)is the same as stream.fail().
  • if (stream)is the same as !stream.fail().
The latter two work via operator overloading, in this case, operator! and operator bool.

If you read the above, you'll see that my std::cout << "EOF detected." was not technically correct. It should have been y std::cout << "Input stream failure detected." The problem is that !stream doesn't test whether the eofbit has been set. It tests whether the failbit has been set. However, the failbit and eofbit go hand-in-hand with the stream extraction operators (std::cin >> some_var)and almost go hand-in-hand with std::getline .

Where they don't go hand-in-hand is in processing a text file in which the last line in the file is not terminated by a newline. On reading that last line, the contents are read and the eofbit is set, but the failbit is not set. The next time around, the eofbit is still set, nothing is read, and that makes the failbit get set. This leads to the following very common and very simple idiom for processing a text file line by line:
C:
std::ifstream stream (input_file_name);
std::string line;
while (std::getline(stream, line)) {
   process_input_line (line);
}
 
  • #23
1,316
104
code=c]
while( w1 != "1"){}
[/code]
First, there's a different between
char w1;
w1!=1;
w1!='1'; // note the single quotaion mark
w1!="1"; // double quation mark

w1!=1; 1 here is an integer value 1
w1!='1'; '1' is an integer valued 49, refering ASCII character '1'
w1="1" as Mark44 pointed out are refering to some memory address in the computer where there are two character
'1' and 0, ascii zero.
"1" is a string
'1' is a char
1 is an integer
 
  • #24
1,316
104
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...
Unlike Pascal or Basic, in C there's no different between char and integer.
char C;
cin >> c
cin would expect an integer value.
If you type 1
cin would translate it to ascii 1, not ascii 49, character '1'

Okay.., now we get to your problem

C:
string ans;
do{
  string w1;
  do { // we'll break the loop with the break statement later.
    cout<< "Would you like to calculate: "<<endl <<
           "1. integral" <<endl <<
           "2. derivative";
    cin>>w1;
    if(w1=="1") break;
    if(w1=="2") break;
    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)"; // there's no point to ask the user for y/n, just ask if the user type y
  cin>>ans;
  if (ans=="y") continue;
  if (ans=="Y") continue;
  break;
}
 
  • #25
D H
Staff Emeritus
Science Advisor
Insights Author
15,393
685
Unlike Pascal or Basic, in C there's no different between char and integer.
Unlike C, in C++ there's a big difference between std::string and primitive types. Your last two posts are a bit off-topic. The code in this thread is C++ code, not C code, and it uses std::string, not char or char*.
 

Related Threads on Infinite loop problem C++ for char instead of int

Replies
2
Views
2K
  • Last Post
Replies
4
Views
3K
  • Last Post
Replies
10
Views
1K
  • Last Post
Replies
2
Views
2K
Replies
1
Views
2K
Replies
14
Views
825
  • Last Post
Replies
3
Views
1K
  • Last Post
Replies
3
Views
3K
  • Last Post
Replies
1
Views
2K
Top