Infinite loop problem C++ for char instead of int

1. Jun 1, 2015

ChrisVer

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. Jun 1, 2015

rootone

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. Jun 1, 2015

ChrisVer

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 ).
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. Jun 1, 2015

ChrisVer

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. Jun 1, 2015

ChrisVer

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) { //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. Jun 1, 2015

rootone

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.

7. Jun 1, 2015

my2cts

w1!=1 || w1!=2 is always true.

8. Jun 1, 2015

ChrisVer

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;
}

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.

9. Jun 1, 2015

rootone

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 .

10. Jun 1, 2015

ChrisVer

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
11. Jun 1, 2015

ChrisVer

I fixed it- it needed && instead of || both in while and if....
However I don't understand why....

12. Jun 1, 2015

wle

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

13. Jun 1, 2015

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

14. Jun 1, 2015

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

15. Jun 1, 2015

ChrisVer

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 $K \ne 1$ and $K \ne 2$, that is the set $K \in \{ 0,3,4,... \}$

The second enters the while if $K \ne 1$ or $K \ne 2$. 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 $K \ne 2$ so it can enter from that door, although that would violate the $K\ne 1$... 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. Jun 1, 2015

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.

17. Jun 2, 2015

meBigGuy

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. Jun 2, 2015

newjerseyrunner

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
}

19. Jun 2, 2015

FactChecker

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
}

20. Jun 3, 2015

D H

Staff Emeritus
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;
}
}

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

21. Jun 4, 2015

ChrisVer

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

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

I don't understand this check either... The syntax is similar though to the link @wle posted.

22. Jun 4, 2015

D H

Staff Emeritus
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".

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.

Think of prompt as a string variable. I was being lazy and didn't bother to cut and paste your prompt.

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.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:
Code (C):

std::ifstream stream (input_file_name);
std::string line;
while (std::getline(stream, line)) {
process_input_line (line);
}

23. Jun 4, 2015

Stephanus

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. Jun 4, 2015

Stephanus

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

Code (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;
}

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. Jun 4, 2015

D H

Staff Emeritus
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*.