Infinite loop problem C++ for char instead of int

In summary, the 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.
  • #1
ChrisVer
Gold Member
3,378
464
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:
Technology news on Phys.org
  • #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?
 
  • #3
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
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
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
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
w1!=1 || w1!=2 is always true.
 
  • #8
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)...

my2cts said:
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
I got to 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
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
I fixed it- it needed && instead of || both in while and if...
However I don't understand why...
 
  • #12
ChrisVer said:
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
ChrisVer said:
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
ChrisVer said:
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
Mark44 said:
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
ChrisVer said:
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
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
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
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
First, a side remark:
Mark44 said:
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.

wle said:
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
newjerseyrunner said:
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...

D H said:
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

D H said:
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
ChrisVer said:
D H said:
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
ChrisVer said:
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, referring ASCII character '1'
w1="1" as Mark44 pointed out are referring 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
ChrisVer said:
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
Stephanus said:
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*.
 
  • #26
Stephanus said:
Unlike Pascal or Basic, in C there's no different between char and integer.
On many systems a char is one byte and an int is four bytes. So, no difference?
Stephanus said:
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'
That's not true. Assuming that you meant "cin >> C" on your second line, to go with how you declared C on the line above, cin is expecting a character. If you press '1' on the keyboard, the ASCII code for '1' is what is stored in C. If you look at the bit pattern it would be the bit pattern for 49, the ASCII code for the character '1', not the numeral 1.
 
  • #27
Mark44 said:
On many systems a char is one byte and an int is four bytes. So, no difference?

That's not true. Assuming that you meant "cin >> C" on your second line, to go with how you declared C on the line above, cin is expecting a character. If you press '1' on the keyboard, the ASCII code for '1' is what is stored in C. If you look at the bit pattern it would be the bit pattern for 49, the ASCII code for the character '1', not the numeral 1.

I mean in C or C++ we can increment char, multiply char, substract char as in integer. We can't do that in Pascal. We have to use Byte type.
But printing char in C or C++ is different than printing integer. It depends on the format you choose.
In Basic there's no Char variable. But string is close enough to char in basic.
 
  • #28
Mark44 said:
That's not true. Assuming that you meant "cin >> C" on your second line, to go with how you declared C on the line above, cin is expecting a character. If you press '1' on the keyboard, the ASCII code for '1' is what is stored in C. If you look at the bit pattern it would be the bit pattern for 49, the ASCII code for the character '1', not the numeral 1.
I'm sorry, I'm sorry.
I code in C, seldom code in C++, I guess I should be more careful when posting.
So for the true 1 not '1' I think we should use int
int C;
cin >> C;
I'm open to suggestions here.
 
  • #29
That's why I used a string instead, because I didn't want to have the problem between whether the program expects an integer or a character.
In particular to avoid getting in an infinite loop if by any mistake the user typed "Q" instead of "1" when the program expects an integer value. Setting the input as a string can contain both chars or ints. Or another way is what was written in the link posted by wle and also used by Mark44.
 
  • #30
I think this is for the cin is a stream of data. When we sent that data to a memory address that keep for int, char, double, the compiler make the best he can. But a string is a pointer. We cannot sent a stream to a pointer. We need a buffer like char[50] to keep strings from cin.
Note that on 64bit linux cin transfers 2byte characters.
 

1. What is an infinite loop in C++?

An infinite loop in C++ is a loop that continues to run indefinitely, without ever terminating. This can happen if the loop condition is always true, or if there is no way for the loop to end, such as not having a break statement.

2. How does using char instead of int cause an infinite loop in C++?

In C++, char is a data type that stores a single character, while int is a data type that stores a whole number. When using char in a loop condition, it will always evaluate to true because a character is always considered a non-zero value. This will cause the loop to continue running indefinitely, resulting in an infinite loop.

3. How can I fix an infinite loop caused by using char instead of int in C++?

To fix an infinite loop caused by using char instead of int in C++, you can either change the data type to int or add a break statement within the loop to terminate it. It is important to carefully consider the data type used in the loop condition to avoid creating an infinite loop.

4. What are the consequences of an infinite loop in C++?

An infinite loop in C++ can cause the program to crash, as it will continue to use system resources without ever terminating. This can also lead to unexpected results and errors in the program. It is important to avoid infinite loops to ensure the proper functioning of the program.

5. How can I prevent an infinite loop in C++?

To prevent an infinite loop in C++, make sure to carefully consider the loop condition and ensure that it will eventually evaluate to false. Also, make sure to include a break statement or other condition that will cause the loop to terminate. It is also helpful to use debugging tools to identify and fix any potential infinite loops in the code.

Similar threads

  • Programming and Computer Science
Replies
12
Views
1K
  • Programming and Computer Science
2
Replies
66
Views
4K
  • Programming and Computer Science
Replies
5
Views
2K
Replies
10
Views
956
  • Programming and Computer Science
Replies
2
Views
871
  • Programming and Computer Science
Replies
6
Views
8K
  • Programming and Computer Science
Replies
5
Views
881
  • Programming and Computer Science
Replies
12
Views
1K
  • Programming and Computer Science
3
Replies
89
Views
4K
  • Programming and Computer Science
Replies
3
Views
3K
Back
Top