C++ switch statement odd output - c++

I'm working on a final assignment for an Intro to C++ course. What I've coded so far works, but it's producing some interesting output that I'm looking for clarification on. Here's my code:
(Caveat: Yes, I know using void main() sucks, but we're using Visual Studio in class, and this is the instructors preference.)
#include <string>
#include <iostream>
using namespace std;
void conversion(int);
void main()
{
int decimal_number, answer;
cout << "Please enter a whole decimal number (e.g. 20): ";
cin >> decimal_number;
if (decimal_number == 0)
{
answer = 0;
cout << "The hexadecimal value of your number is: " << answer;
getchar();
getchar();
}
else if (decimal_number < 0)
{
cout << "INVALID ENTRY" ;
getchar();
getchar();
}
else if (decimal_number > 0)
{
conversion(decimal_number);
}
getchar();
getchar();
}
void conversion (int decimal_number)
{
int count = 0, remainder, reverse_order;
char hexadecimal_number[10] = { NULL };
while (decimal_number != 0)
{
remainder = decimal_number % 16;
switch (remainder)
{
case 0:
hexadecimal_number[count] = '0';
count++;
break;
case 1:
hexadecimal_number[count] = '1';
count++;
break;
case 2:
hexadecimal_number[count] = '2';
count++;
break;
case 3:
hexadecimal_number[count] = '3';
count++;
break;
case 4:
hexadecimal_number[count] = '4';
count++;
break;
case 5:
hexadecimal_number[count] = '5';
count++;
break;
case 6:
hexadecimal_number[count] = '6';
count++;
break;
case 7:
hexadecimal_number[count] = '7';
count++;
break;
case 8:
hexadecimal_number[count] = '8';
count++;
break;
case 9:
hexadecimal_number[count] = '9';
count++;
break;
case 10:
hexadecimal_number[count] = 'A';
count++;
break;
case 11:
hexadecimal_number[count] = 'B';
count++;
break;
case 12:
hexadecimal_number[count] = 'C';
count++;
break;
case 13:
hexadecimal_number[count] = 'D';
count++;
break;
case 14:
hexadecimal_number[count] = 'E';
count++;
break;
case 15:
hexadecimal_number[count] = 'F';
count++;
break;
default:
cout << decimal_number << "+++ " << hexadecimal_number;
cout << "INVALID ENTRY";
getchar();
getchar();
}
decimal_number = decimal_number / 16;
}
cout << "The hexadecimal value of your number is: ";
for (reverse_order = count -1; reverse_order >= 0; reverse_order--)
{
cout << hexadecimal_number[reverse_order];
}
getchar();
getchar();
}
So, like I said: my code works. I can take any number input as a decimal, and convert it to its hexadecimal equivalent. However, I've found that I've had to include an IF statement within the main function of the code, because if the user inputs anything other than a decimal number into the decimal_number variable, the program will store a string of decimal numbers, into decimal_number, and I have no idea where those numbers come from. They don't appear to be the ASCII equivalents of anything.
... If none of this makes any sense, I'm sorry. just input cout << decimal_number after the line cin >> decimal_number, then run the code and see what weird number comes out. I hope that makes things clearer.
Anyway, my instructors stumped, and I'm stumped. I've got the above workaround in place that the instructor will accept, but for my own sanity, I just want to figure out what's going on. Any help or pointers is appreciated. Cheers!

You can test whether the result of cin >> decimal_number succeeded, like
if(!(cin>>decimal_number))
throw std::runtime_error("Oops, not a decimal number!");
This is a bit too extreme, you can also validate the input:
while(!(cin>>decimal_number))
{
std::cout << "Not decimal, input again ";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
If you're not performing this kind of validation, then you leave the stream in an invalid state whenever reading a non-decimal, and the variable you think you're reading into will end up un-initialized.

However, I've found that I've had to include an IF statement within the main function of the code, because if the user inputs anything other than a decimal number into the decimal_number variable, the program will store a string of decimal numbers, into decimal_number, and I have no idea where those numbers come from.
Well, you did not initialise decimal_number to anything, and you do not have any error checking around the cin >> decimal_number call. So I'm not sure what else you expected but an unspecified value for decimal_number!
Your instructor should know this. It's worrying that, on top of teaching you to write code that is ill-formed per the International Standard (void main!!), they failed to discover this problem or note that you have no error checking.

What's the point of the assignment?
The conversion can be simplified to:
cout << hex << decimal_value << endl;
Or if you need it in a string:
std::string convert_decimal_to_hex_string(int decimal_value)
{
std::ostringstream output;
output << hex << value;
return output.str();
}
I believe the class should show you how to use std::string and existing language features (such as the hex manipulator). Using char for a string is dangerous.
Also, since you don't know the size or limit of the decimal values, you will need to dynamically allocate (i.e. during run-time) the array holding the characters. Think about allocating 2 characters and entering the value 1024; buffer overflow.
Change your program to use std::string. Refrain from character (C-Style) arrays.

Related

A few coding convention/standard practice questions

I have this function that takes a string from main. The string contains all of the valid characters that a user can input from some menu options. Function will put character input into a variable and be compared to each character of the string. Compare input variable to the string characters until valid input is entered.
My question is, what is the best way to implement this loop? I don't like using while (true) with a return in the middle because it looks like an infinite loop with an exception in the middle, which makes it slightly harder to read, but I'm not sure how else I can do what I want it to do. What's the best practice for achieving my goal? Thanks.
char getValidKey(string validKeys)
{
char letter;
while (true) {
cout << "Operation ? ";
cin >> letter;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
for (int i = 0; i < validKeys.length(); i++) {
if (letter == validKeys[i])
return letter;
}
cout << "Error. Invalid input.\n";
}
}
Also, I have a switch statement with multiple returns. Is it more common/preferred to assign calculations to a variable and have one return at the end or is this way generally okay?
string opStr;
switch (myOperation) {
case 1:
opStr = "RIGHT";
break;
case 2:
opStr = "LEFT";
break;
case 3:
opStr = "CENTER_ONLY";
break;
case 4:
opStr = "CENTER_MISSING";
break;
default:
opStr = "Error. Invalid input.";
break;
}
return opStr;
OR
switch (myOperation) {
case 1:
return "RIGHT";
break;
case 2:
return "LEFT";
break;
case 3:
return "CENTER_ONLY";
break;
case 4:
return "CENTER_MISSING";
break;
default:
return "Error. Invalid input.";
break;
}
For the first case, refactor your code in smaller self-contained functions, and it becomes clear to understand the logic of getValidKey even from a while(true):
char isKeyValid(char x, const string& validKeys)
{
return validKeys.find(x) != string::npos;
}
char readCharFromCin()
{
char letter;
cout << "Operation ? ";
cin >> letter;
cin.ignore(numeric_limits<streamsize>::max(), '\n');
return letter;
}
char getValidKey(const string& validKeys)
{
while (true)
{
const char key = readCharFromCin();
if(isKeyValid(key, validKeys)) return letter;
cout << "Error. Invalid input.\n";
}
}
For the second case, avoid break and simply return from your switch. Make the function containing the switch only do one thing.
string switchOperation(int myOperation)
{
switch (myOperation)
{
case 1: return "RIGHT";
case 2: return "LEFT";
case 3: return "CENTER_ONLY";
case 4: return "CENTER_MISSING";
}
return "Error. Invalid input.";
}
Also, try to maximize usage of const and pass string instances you're only reading by const& to avoid unnecessary copies.

A few errors - trying to convert Roman entry to Decimal

Just going to change my question for now - I could just use some guidance as to why I have three compiler errors in my program, not quite sure what I did wrong/am missing - I have added comments into the code just to state where they are. Thanks
#include <iostream>
#include <string>
using namespace std;
class romanType
{
public:
void setRomanNum(string store);
// this function will store the Roman numeral
int convertNum(char rNum);
// this function will convert the Roman numeral to a decimal
void decimalPrint(int total);
// this function will print the decimal number
void romanPrint(char rNum);
// this function will print the Roman numeral
int getNum(char letter);
// this function will get the number input
romanType(int store);
//Constructor with parameter
romanType();
char roman[7];
string num;
int length = 0;
string dNum;
int equals;
};
romanType::romanType(int store)
{
dNum = 1;
}
void romanType::setRomanNum (string store)
{
dNum = store;
}
void romanType::romanPrint(char rNum)
{
cout << "The Roman numeral is: " << roman << endl;
}
void romanType::decimalPrint(int total)
{
cout << "The Decimal number is: " << equals << endl;
}
int romanType::convertNum (char rNum)
{
int letter;
int totalNum = 0;
for (int i = 0; i< dNum.length(); i++)
// "loop will run at most once (loop increment never executed)"?
{
switch (roman[i])
{
case 'M':
totalNum+= 1000;
break;
case 'D':
totalNum += 500;
break;
case 'C':
totalNum += 100;
break;
case 'L':
totalNum += 50;
break;
case 'X':
totalNum += 10;
break;
case 'V':
totalNum += 5;
break;
case 'I':
totalNum += 1;
break;
}
totalNum = totalNum + letter;
equals = totalNum;
return equals;
}
};
// "control may reach end of non-void function"
int main()
{
romanType output;
int rNumeral;
char entry;
cout << "Please enter a Roman numeral (Capitalized only): " << endl;
cin >> rNumeral;
cout << "Print Decimal or Roman Numeral? Type 1 for Decimal, 2 for Roman Numeral: " << endl;
cin >> entry;
if (entry == '1')
{
cout << "You chose to view the decimal conversion." << endl;
output.decimalPrint(rNum);
// How do I output the decimal conversion from the void romanType::decimalPrint(int total) function?
}
else if (entry == '2')
{
cout << "You chose to view the Roman numeral." << endl;
output.romanPrint(rNumeral);
}
else
cout << "Error: bad input" << endl;
return 0;
exit(1);
}
The mistake in the algorithm is that the Roman system is non-positional. See en.wikipedia.org/wiki/Roman_numerals and en.wikipedia.org/wiki/Subtractive_notation. You can't just add up consequent digits (i.e. letters) but you also have to identify and account for the cases when subtraction takes place.
why am I getting the error "no matching constructor for initialization of "roman type"?
Because the only provided constructor is not the default one, but takes one parameter of type int. Since such a constructor was provided, the default constructor wasn't generated by the compiler. Define romanType::romanType() or change the existing one to romanType::romanType(int i = 0) (add a default parameter). See Is there an implicit default constructor in C++? and why default constructor is not available by default in some case
expected expression?
Provide braces around the preceding else block. More than one statement -> braces required.
if (entry == '1') {
cout << "You chose to view the decimal conversion." << endl;
output.decimalPrint(rNum);
} else if (entry == '2')
cout << "You chose to view the Roman numeral." << endl;
output.romanPrint(rNumeral);
}
"control may reach end of non-void function"
This is a warning only, but it will turn into an error if you include the -Werror flag that tells the compiler to treat all warnings as errors.
Ok I was wrong on this one. Actually the trick is that it is (in theory) possible that the romanType::convertNum(int) function follows a route where the for loop will never get executed and thus no return statement will be executed either. That's bad since the function is declared to return int hence there must be present an explicit return statement that (surprise) would return a value. Move the return out of the loop. This error is also closely related to the next one, discussed below.
"loop will run at most once (loop increment never executed)"?
This is because the return statement is placed incorrectly: inside the for loop not outside of it, and inside the function body. Hence the loop runs once and the function returns. Move the return statement. Credit to #ATN_LR_boom for noticing this!
Also, please get in the habit of formatting code properly. It will save you a lot of headache down the way.
Other than that, I'd use a std::map for the conversion function, it's shorter and more clear to the reader compared to the switch statement. Something along the lines of
int romanType::convertNum(int rNum) {
const static std::map<char, int> conversion = {
{'M', 1000},
{'D', 500},
// more mappings
{'I', 1}
};
if ((auto it = conversion.find(rNum)) != conversion.end())
return it->second;
else
return -1;
}
Your logic for the switch is wrong, try something like this:
int totalNum = 0;
for (int i = 0; i< dNum.length(); i++)
// the loop ran once because you were returning values when catching a letter
{
switch (roman[i])
{
case 'M': // roman[i] is a char, the cases should try to catch chars
totalNum += 1000; // increment your global number
break;
case 'D':
totalNum += 500;
break;
...
}
return totalNum;

C Looping a switch using Y/N options. Choosing N doesn't close the program

I've only started coding C. (freshman here, no experience, only basic html)
For some reason, whenever I choose the option 'N', it just creates a new line.
At my second scanf whenever I change my '%s', the result changes.
When I use, "%d",after entering a number, it continuously enter "Choose a number between 1-4". If I use "%c",after entering a number, it will skip directly to the loop. Help?
#include <stdio.h>
int main()
{
int n, ok = 0;
char input;
while (ok == 0)
{
printf("Choose a number between 1-4\n");
scanf("%d", &n);
switch (n)
{
case 1:
printf("You've chosen number 1");
break;
case 2:
printf("You've chosen number 2");
break;
case 3:
printf("You've chosen number 3");
break;
case 4:
printf("You've chosen number 4");
break;
default:
printf("You have chosen an invalid number");
}
printf("\nInput again? (Y/N)\n");
scanf("%s", &input);
if (input=='n'||input=='N')
{ok++;}
else if (input=='Y'||input=='y')
{printf("\n");}
}
getchar();
getchar();
}
change
scanf("%s", &input);
to
scanf(" %c", &input);
and get rid of getchar()s at the end. It will work as you wanted. %c specifier reads a char character, but it does not omit whitespace characters. Putting space onto scanf's format will deal with whitespaces and read first non-whitespace char. To be more clear, see what man page says about %c format specifier:
(...) The usual skip of leading white space is suppressed. To skip
white space first, use an explicit space in the format.
However, if you are really learning C++, stick with cin/cout, rather than scanf/printf's.
Here's how your program would look like if you would replace printf/scanf with cin/cout. If you've done that previously, you wouldn't had that kind of trouble.
#include <iostream>
using namespace std;
int main()
{
int n, ok = 0;
char input;
while (!ok)
{
cout << "Choose a number between 1-4\n";
cin >> n;
switch (n)
{
case 1:
cout << "You've chosen number 1";
break;
case 2:
cout << "You've chosen number 2";
break;
case 3:
cout << "You've chosen number 3";
break;
case 4:
cout << "You've chosen number 4";
break;
default:
cout << "You have chosen an invalid number";
}
cout << "\nInput again? (Y/N)\n";
cin >> input;
if (input=='n' || input=='N')
{
ok++;
}
else if (input=='Y' || input=='y')
{
cout << "\n";
}
}
return 0;
}

getline() omits first letter of my output from array.

I'm coding a simple Mad Libs program for school. The code I'm posting iterates through an array searching for certain prompts. Once found it uses the prompt to ask a question and records the answer. The array that holds my answers however, is omitting the first letter of every word except for the very first variable. Here is my code and a copy of the output from the array. It's shite I know, but I'm learning.
char buffer[256];
int y = 0;
//iterates through array looking for answers
for(int i = 0;i <= 256;i++)
{
if(storyArray[i][0] == '<' && isalpha(storyArray[i][1]))
{
for(int x = 0; storyArray[i][x]; x++)
{
switch(storyArray[i][x]){
case '<':
cout << "\t";
x++;
putchar(toupper(storyArray[i][x]));
break;
case '>':
cout << ": ";
cin.ignore();
cin.getline(buffer,256);
strcpy(answerArray[y],buffer);
y++;
break;
case '_':
cout << " ";
break;
default:
cout << storyArray[i][x];
break;
}
}
}
}
Output:
Arrayitem1
rrayitem2
You're telling it to miss the first character. That's what this does:
cin.ignore();
Take that out and you'll be fine.

How to prevent the user from entering more than one character in the below sample code?

I am facing problem in the below code. If the user enter more than one charater then my loop gets executed number of times equal to the length of the string entered by the user. My code is written in GNU c/c++ compiler.
Thanks in advance.
int continue_option()
{
char c;
loop:
fflush(stdin);
cin.ignore();
cout<<"\n\n\t\t\t\tPress (Y/y) - Continue / Press (N/n) - Exit :";
cin>>c;
if(c=='y'||c=='Y')
{
system("clear");
}
else if(c=='n'|| c=='N')
{
exit(0);
}
else
{
printf("\n\t\t\t\tInvalid Option.Try Again.....");
goto loop;
}
fflush(stdin);
}
First thing, don't use jumps. They are old style, and they make Dijkstra spin in his grave, on top of all the other bad consequences. I don't mean "vintage", I really mean old in the bad sense.
As of your question, I'd rather put the result in a std::string and only consider the first character in there:
std::string input;
std::cin >> input;
switch (input[0]) {
case 'y':
case 'Y':
//your code
break;
case 'n':
case 'N':
exit(0);
default:
std::cout << "Invalid text" << std::endl;
}
I would also refrain from using exit(), I'd rather rely on a function's return value to finally cause a return 0; in the main(), or some equivalent technique.
You can't stop the user from typing more than one character.
What you can do is ignore the rest of the line. You have already use cin.ignore() which ignores one character. You can use cin.ignore(large number) to ignore up to the large number or the end-of-line, whichever appears first.
Unlike flushing output files, fflush(stdin) doesn't really do anything.
Try using cin.get() or getch() to read just one character at a time. Also, I guess you'd be better off replacing the whole thing with a simple loop like:
char ch = '\0';
do
{
ch = getch();
}while((tolower(ch) != 'y') || (tolower(ch) != 'n'))
if(tolower(ch) == 'y')
{
//additional handling
}
else
{
exit(0);
}
Not exactly the same behavior, but should put you on track:
#include <iostream>
#include <iomanip>
bool is_valid_answer(char c)
{
switch(c)
{
case 'y':
case 'Y':
case 'n':
case 'N':
return true;
default:
return false;
}
}
bool continue_option()
{
std::cout << "Press (Y/y) to continue, (N/n) to exit: " << std::flush;
char c = '\0';
while (std::cin.get(c) && !is_valid_answer(c));
return ((c == 'y') || (c == 'Y'));
}
int main()
{
std::cout << "Continue option: " << continue_option() << std::endl;
}