String method crashes program - c++

Alright so i have two identical string methods...
string CreateCust() {
string nameArray[] ={"Tom","Timo","Sally","Kelly","Bob","Thomas","Samantha","Maria"};
int d = rand() % (8 - 1 + 1) + 1;
string e = nameArray[d];
return e;
}
string CreateFood() {
string nameArray[] = {"spagetti", "ChickenSoup", "Menudo"};
int d = rand() % (3 - 1 + 1) + 1;
string f = nameArray[d];
return f;
}
however no matter what i do it the guts of CreateFood it will always crash. i created a test chassis for it and it always fails at the cMeal = CreateFood();
Customer Cnow;
cout << "test1" << endl;
cMeal = Cnow.CreateFood();
cout << "test1" << endl;
cCustomer = Cnow.CreateCust();
cout << "test1" << endl;
i even switched CreateCust with CreateFood and it still fails at the CreateFood Function...
NOTE: if i make createFood a int method it does work...
Also guys even if i changed CreateFood to just COUT a message and nothing more it still crashed...

Take out the + 1 on both of them, you access arrays starting from 0:
int d = rand() % (8 - 1 + 1); // 0-7, not 1-8
int d = rand() % (3 - 1 + 1); // 0-2, not 1-3
Otherwise you're accessing a non-existent element, and this is undefined behavior. (That means it could appear to work, like in CreateCust, crash like in CreateFood, do nothing, or do anything.)
I'm not sure what the purpose of subtracting 1 then adding 1 is. In any case, now is the perfect time to learn: Don't Repeat Yourself. Even if you do something just twice, make a function out of it, it'll be less cryptic and more concise:
int random(int min, int max)
{
return rand() % ((b - a) + 1) + a;
}
This is a simple function that returns a random number between a and b, inclusive. (Means it can include both a and b in the results.) Now your code reads:
// I'll leave CreateCust up to you
string CreateFood(void)
{
string nameArray[] = {"spagetti", "ChickenSoup", "Menudo"};
int d = random(0, 2); // either 0, 1, or 2, randomly
string f = nameArray[d];
return f;
}
And you'll see even just one function makes it much easier to read; your goal is to make your code easy to read by humans. Also, this is much more concise:
string CreateFood(void)
{
string nameArray[] = {"spagetti", "ChickenSoup", "Menudo"};
return nameArray[random(0, 2)];
}
Another bad thing to do is hardcode magic numbers into your program. For example, why 3 or 8? It can be deduced those are array sizes, but that doesn't stand on its own. What you might want is something like:
string CreateFood(void)
{
const size_t ArraySize = 3; // 3 elements, 0-2
string nameArray[ArraySize] = {"spagetti", "ChickenSoup", "Menudo"};
// ^ Ensure it matches
return nameArray[random(0, ArraySize - 1)];
}
Now the range for the number number makes sense up front.
The rest may be a bit advanced (which you won't understand until you get to templates), but shows how we might go on:
template <typename T, size_t N>
char (&countof_detail(T (&)[N]))[N];
#define countof(pX) sizeof(countof_detail(pX))
This nifty tool will give you the number of elements in an array. The code might turn into this
string CreateFood(void)
{
string nameArray[] = {"spagetti", "ChickenSoup", "Menudo"};
// ^ no explicit size
return nameArray[random(0, countof(nameArray) - 1)];
}
We got rid of any numbers altogether, you can just manipulate the array as you please. Lastly, we're repeating ourselves again: getting a random element from an array. We should make a function for that:
template <typename T, size_t N>
T& random_element(T (&pArray)[N])
{
return pArray[random(0, N - 1)];
}
This returns a random element from any array. Your function would then simply be:
string CreateFood(void)
{
string nameArray[] = {"spagetti", "ChickenSoup", "Menudo"};
return random_element(nameArray);
}
Note in this refactoring (refactoring is taking code and factoring it into new, simpler parts) it reads much better: To get a food, we have an array of foods and we pick one at random.
Keep this kind of stuff in mind while you work, and as you learn C++ you can make better code. Anytime you repeat a task that isn't trivial, make it a function. Suddenly that task is trivial, because you don't care about how the function works (that's in the function), just what the function does (that's the function name).

The crash is happening because you are accessing an invalid index. This is because array indexes start from 0 and not 1, so you don't want to add a 1 to the rvalue of the modulus operator.
Here is a neat trick that you can use to make your code a little more maintainable:
template <class T>
T getRandElem( const T[] arr )
{
return arr[ rand() % ( sizeof(arr) / sizeof((arr)[0]) ) ];
}
string CreateCust(){
static string nameArray[] = {"Tom","Timo","Sally","Kelly","Bob","Thomas","Samantha","Maria"};
return getRandElem<string>( nameArray );
}
string CreateFood(){
static string nameArray[] = {"spagetti", "ChickenSoup", "Menudo"};
return getRandElem<string>( nameArray );
}

Look here:
int d = rand() % (8 - 1 + 1) + 1;
This will return a number between 1 and 8 inclusive. What you need is this:
int d = rand() % 8;

You're going outside the bounds of the array. The array object begins at 0.

I don't understand why you have
int d = rand() % (8 - 1 + 1) + 1;
Why not just use
int d = rand() % 8;

I think modern c++ compilers will still let you use an old C trick for static arrays:
string CreateFood()
{
char* nameArray = {"spagetti", "ChickenSoup", "Menudo"};
// note the trick to get the compiler to count the array elements for you:
int d = rand() % (sizeof(nameArray) / sizeof(nameArray[0]));
return std::string( nameArray[d] );
}

Related

Incremented variable "never used"?

I'm kind of inexperienced with C++, and I'm converting a program that I wrote in C to C++. I have a RollDice function that takes numbers that I read in from a text file and uses them to generate the number. This is the function in C:
void rollDice(Move *move, GameState *game_state) {
int diceNum1 = 0;
int diceNum2 = 0;
int randomNumber1 = 0;
int randomNumber2 = 0;
randomNumber1 = game_state->randomNums[game_state->current_roll]; //gets the random number from the array randomNum (which holds the numbers from the text file), at index "current_roll"
game_state->current_roll++; //increments so the next random number will be the next number in the array
diceNum1 = 1 + (randomNumber1 % (1 + 6 - 1));
randomNumber2 = game_state->randomNums[game_state->current_roll];
game_state->current_roll++;
diceNum2 = 1 + (randomNumber2 % (1 + 6 - 1));
move->dice_sum = diceNum1 + diceNum2;
printf("You rolled a %d!\n", move->dice_sum);
}
This works just how I want it to when I run it. Now, when converting my program to C++ I had to change things around. My parameters are now pass by reference and I made a vector to store the list of random numbers from the text file:
void rollDice(Move& move, GameState& game_state) {
std:: vector<int> randomNums = game_state.getRandomNums();
int current_roll = game_state.getCurrentRoll();
int diceNum1 = 0;
int diceNum2 = 0;
int randomNumber1 = 0;
int randomNumber2 = 0;
randomNumber1 = randomNums.at(current_roll);
current_roll++;
diceNum1 = 1 + (randomNumber1 % (1 + 6 - 1));
randomNumber2 = randomNums.at(current_roll);
current_roll++; //this line is grayed out and says "this value is never used"
diceNum2 = 1 + (randomNumber2 % (1 + 6 - 1));
move.dice_sum = diceNum1 + diceNum2;
std:: cout << "You rolled a " << move.dice_sum << "!\n";
}
My code is telling me that the second time I increment current_roll it is unused. This didn't happen for my C code, so why is it happening here and how can I fix it? I'm completely lost.
It's never used because you write to the variable, but never read from it. Having a variable that you never read is effectively meaningless.
Presumably your game_state.getCurrentRoll function returns an integer, when you store this, you store the value (rather than a reference to the value), thus incrementing it doesn't increment the current roll inside the game_state, instead you should add a function to your game_state called makeRoll for example which increments the game_states internal current_roll value.
This is different from your C code which increments the current_roll value directly using game_state->current_roll++ (alternatively you could make game_state.current_roll public and increment it the same way as in your C code).
From your comment I assume you have some class:
class GameState {
private:
int current_roll;
...
public:
int getCurrentRoll() {
return current_roll;
}
...
}
All you'd need to do is add another function to your class to increment the current_roll:
class GameState {
private:
int current_roll;
...
public:
int getCurrentRoll() {
return current_roll;
}
void makeRoll() {
current_roll++;
}
...
}
Then you can call it as normal.
Regarding your new issue in the comments regarding the error:
parameter type mismatch: Using 'unsigned long' for signed values of type 'int'.
This is because the signature of at is std::vector::at( size_type pos ); That is, it expects a value of type size_type which is an unsigned integer type, rather than int as you're using which is signed. This post may be helpful.

C++ error: invalid conversion from 'int' to 'const char*' [-fpermissive]|

First of all, i need to say, that i am a complete noob.
I am trying to make some naval combat simulation to do that i created a random letter and number generator
Here's the code.
By the way, what i want to achieve is to have only one variable (Guess) to be confronted with the ship places that the user specified.
char letters[]= {'A','B','C','D','E','F','G','H','I','L'};
\\ lots of code
//RandomAI
int G = rand() % 10 + 1;
int nOut = rand() % 10 + 1;
char lOut = letters[G];
string Guess = lOut + nOut;
return 0;
string Guess = lOut + nOut; adds an int and char types which does not produce a std::string. One way to address this is to create a string and then append to it:
std::string guess = lOut + std::to_string(nOut);
This will solve your compiler error, but you still have a logic error here:
int G = rand() % 10 + 1;
rand() % 10 + 1 will produce a value between 1 and 10 inclusive. You want a number between 0 and 9 inclusive, because indices in C++ begin at 0, not 1. So drop the +1 portion:
int G = rand() % 10;
Otherwise you may accidentally attempt to access an out-of-bounds index in letters
Assuming you wanted an output string like 'A7' or 'F2', one way to achieved this would be to convert everything to string (since you can't add and int and a char). For those who don't have access to C++11 and std::to_string() yet, you could use:
char letters[]= {'A','B','C','D','E','F','G','H','I','L'};
int G = std::rand() % 10;
int nOut = std::rand() % 10;
char lOut = letters[G];
std::stringstream ss;
ss << nOut;
std::string Guess = lOut + ss.str();

read data from a custom collection (struct Array)

i am a beginner in c++ ..so please help me get this right.
trying to read from the collection, in one version of implementation I have tried ,there was some bippings from the console, another test.. displays numbers so its probably the pointer to the string...
the code is as follows
DataCollection.h
typedef struct _DataC
{
char* buffer;
UINT Id;
} DataC;
void GetDataC( int ArrSize, DataC** DArr );
DataCollection.cpp
#include "DataCollection.h"
void GetDataC( int ArrSize, DataC** DArr )
{
int count = 0;
int strSize = 10;
*DArr = (DataC*)CoTaskMemAlloc( ArrSize * sizeof(DataC));
DataC* CurData = *DArr;
char TestS[] = "SomeText00";
for ( int count = 0; count < ArrSize; count++,CurData++ )
{
TestS[strSize-1] = count + '0';
CurData->Id = count;
CurData->buffer = (char*)malloc(sizeof(char)*strSize);
strcpy(CurData->buffer, TestS);
}
}
test the collection:
int main(void)
{
StpWatch Stw;long ResSw;
DataC* TestDataArr;// maybe use DataC TestDataArr[] instead...
GetDataC(100000, &TestDataArr);
}
how can i read the collection within a loop ?
for...
std::cout<<TestDataArr[count].buffer<<std::endl;
or ?
std::cout<<TestDataArr->buffer<<std::endl;
What is the correct implementation to read each element in a loop?
thanks for your time.
DataC* TestDataArr and DataC TestDataArr[] are the same thing. That said, when you try to reference TestDataArr you may do one of two things:
TestDataArr[index].buffer
or
(TestDataArr + index)->buffer
Because TestDataArr is a pointer you must deference it before you may use any of its members, this is what index does. Using the first method, as an array index, the pointer is dereferenced at index in the array and you then may use . to access members of the object. The second method, index advances the pointer to the memory location but does not dereference the pointer, so you must use -> to then access its members.
So to print the buffer in a loop, you could use either:
std::cout << TestDataArr[count].buffer << std::endl;
or
std::cout << (TestDataArr + count)->buffer << std::endl;
The blipping you mention is probably because of TestS[strSize-1] = count + '0'; where count + '0' creates a character outside of the ASCII range. Some of these characters will cause console beeps.
The problem is in TestS[strSize-1] = count + '0';. When you pass ArrSize == 100 0000 then in the for loop the count + '0' value at a some moment exceeds the range of char and you get a char value in the range [0-31] (non-printable characters). At least use
TestS[strSize-1] = '0' + count % (126 - '0');
The last char of TestS will be changed in the range [48-126] (ASCII printable characters).

Return the result of sum of character arrays

Recently in an interview i was asked a question to write a function which takes two character arrays(integers) as input and returns the output character array.
Function Signature:
char* find_sum(char* a, char* b)
How would one approach this?
Example scenario:
find_sum("12345","32142") = "44487"
Note:
The number of digits can be many(1-100).
u can add huge numbers using the char array approach. however you need to delete the char* after using it every time or use some smart pointer.
char* find_sum(char* a, char* b) {
int lenA = strlen(a), lenB = strlen(b);
int max = lenA > lenB ? lenA : lenB; // Get the max for allocation
char* res = (char*)malloc (max+2);
memset(res, '0', max +1); // set the result to all zeros
res[max+1] = '\0';
int i=lenA - 1, j = lenB - 1, k = max;
for (; i >= 0 || j >=0; --i, --j, --k) {
int sum = 0;
if (i >= 0 && j>=0)
sum = a[i] - '0' + b[j] - '0' + res[k] - '0' ; // add using carry
else if (j >= 0)
sum = b[j] - '0' + res[k] - '0' ; // add the carry with remaining
else if (i >= 0)
sum = a[i] - '0' + res[k] - '0' ;
res[k] = sum % 10 + '0';
res[k-1] = sum / 10 + '0';
}
return res;
}
int main() {
printf (" sum = %s ", find_sum("12345432409240242342342342234234234", "9934563424242424242423442424234"));
return 0;
}
Note: The precondition for the function is the input char arrays should contain only numbers.
The most obvious answer is internally to use something like atoi and sprintf to convert the numbers to integers, do the sum and return the response as a char* However the important thing here is not what the interviewer is asking but why.
In my experience, the interviewer is probably not wanting you to write a hum-dinger of a solution that covers all angles. What they most likely want to get to is what the most common approach would be, and what are the likely limitations of such a function. I.e.:
What happens if your input numbers aren't integers? (e.g. 13.245, 2.3E+7)
What happens if your 'numbers' aren't numbers at all?
What happens if your input integers are really big? (i.e. ~2^31)
How could you detect an error and how would you report it.
How would you allocate memory for the resultant string?
What would the memory allocation imply for the calling code?
What is the efficiency of the function and how could you make it more efficient?
In this way, the interviewer wants to probe your experience of critiquing approaches to problem solving. Naturally, there are many ways of solving this problem. Some of the approaches have side-effects but in certain contexts, these side effects (i.e. integer overflow) may not be greatly important.
Coding is often a trade off between a comprehensive solution and what can be produced quickly (and therefore less expensively) These questions allow the interviewer to get a feel for your understanding of quality - that is, can you design something that is fit for purpose, robust and yet does not take too long to put together - and also your experience of having to identify / resolve common bugs.
You did not mention anything about not using any external command.
We can do this easily on machines that have the bc command. You can add any number of digits:
$ echo "99999999999999999999999999999999+1" | bc
100000000000000000000000000000000
$
We call this bc from the C program. We need to construct the right command line as
echo "n1+n2" | bc
and then use popen to read its result. Below is the function to do that. The code lacks many error checking.
char* find_sum(char* a, char* b) {
int l1 = strlen(a),l2 = strlen(b);
int cmdLen = l1 + l2 + 30; // 30 to accomodate echo,bc and stuff.
char *cmd = malloc(cmdLen);
snprintf(cmd,cmdLen,"echo \"%s+%s\"|bc",a,b);
FILE *fp = popen(cmd, "r");
int max = (l1 > l2) ? l1:l2;
max += 2; // one for additional digit, one for null.
char *result = malloc(max);
fgets(result, max, fp);
return result;
}
Working link
The answer is probably that you have to ask what is returned? Is this a memory allocated string that should be freed by the user or is this a static memory location that is overwritten the next time the function is called?
char* find_sum(char* a, char* b) {
static char buf[MAX_STRING];
...
return buf;
}
or
char* find_sum(char* a, char* b) {
char *buf = malloc(MAX_STRING*sizeof(char));
...
return buf;
}
Giving this answer shows the interviewer that you know more about C than just making an algorithm. (As a side-node: It also shows why a language like java shines in these situations as the garbage collections takes care of freeing the buffer).
Just remember how you did addition in the second grade on the paper.
#include <stdio.h>
#include <string.h>
char *sum(char *a,char *b);
int main()
{
char a[] = "100";
char b[] = "300";
char *c;
c = sum(a,b);
printf("%s",c);
}
char *sum(char *a,char *b)
{
int x,y,z,z2,zLen;
char *result;
x = atoi(a);
y = atoi(b);
z = x + y;
z2 = z;
/* Determine the length of the string now! */
for(zLen = 1; z > 0 || z < 0; zLen++)
z/=10;
result = (char *)malloc(zLen*sizeof(char)+1);
sprintf(result,"%d\0",z2);
return result;
}
Quick and dirty implimentation. Note that I'm not freeing the memory, which is not "ideal". Will fetch you extra brownie points for mentioning that there are no error checks happening here, and no freeing of memory, which is far from ideal in practical situations.
Online Version of Code
Several of the answers mention the use of atoi & itoa functions.
atoi returns int. Your numbers may not fit into an integer data type.
You may try to alleviate the problem (not completely though) using atol, which return a long int, or atoll, which returns a long long int.
Also, itoa is not a standard library function, and hence may not be available on all systems.
Here's another approach. Nothe that I don't like the prototype for find_sum. I'd very much prefer it to be
char *find_sum(char *dst, size_t len, const char *a, const char *b);
letting the caller be responsible for managing resources.
a and b are strings composed of 1 or more digits (and digits only); the result should be freed by caller. Calling find_sum with invalid inputs causes UB :-)
char *find_sum(char *a, char *b) {
char *res;
int alen, blen, rlen;
int carry;
alen = strlen(a);
blen = strlen(b);
rlen = 1 + ((alen > blen) ? alen : blen);
res = malloc(1 + rlen);
if (res) {
int oldlen = rlen;
res[rlen] = 0;
carry = 0;
while (rlen) {
int tmp;
if (alen && blen) tmp = a[--alen] - '0' + b[--blen] - '0';
else if (alen) tmp = a[--alen] - '0';
else if (blen) tmp = b[--blen] - '0';
else tmp = 0;
tmp += carry;
res[--rlen] = '0' + tmp % 10;
carry = tmp / 10;
}
if (res[0] == '0') memmove(res, res+1, oldlen);
}
return res;
}
There's a working version of the function at ideone ( http://ideone.com/O2jrx ).
itoa(atoi(a) + atoi(b), t, 10); if you want to be lazy, where t is a char[MAX_NUMBER_OF_DIGITS].
The real question regards the output array, as mentioned by other users.

Simplifying my program to convert numbers to from one base to another

I'm taking a beginner C++ course. I received an assignment telling me to write a program that converts an arbitrary number from any base between binary and hex to another base between binary and hex. I was asked to use separate functions to convert to and from base 10. It was to help us get used to using arrays. (We already covered passing by reference previously in class.) I already turned this in, but I'm pretty sure this wasn't how I was meant to do it:
#include <iostream>
#include <conio.h>
#include <cstring>
#include <cmath>
using std::cout;
using std::cin;
using std::endl;
int to_dec(char value[], int starting_base);
char* from_dec(int value, int ending_base);
int main() {
char value[30];
int starting_base;
int ending_base;
cout << "This program converts from one base to another, so long as the bases are" << endl
<< "between 2 and 16." << endl
<< endl;
input_numbers:
cout << "Enter the number, then starting base, then ending base:" << endl;
cin >> value >> starting_base >> ending_base;
if (starting_base < 2 || starting_base > 16 || ending_base < 2 || ending_base > 16) {
cout << "Invalid base(s). ";
goto input_numbers;
}
for (int i=0; value[i]; i++) value[i] = toupper(value[i]);
cout << "Base " << ending_base << ": " << from_dec(to_dec(value, starting_base), ending_base) << endl
<< "Press any key to exit.";
getch();
return 0;
}
int to_dec(char value[], int starting_base) {
char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
long int return_value = 0;
unsigned short int digit = 0;
for (short int pos = strlen(value)-1; pos > -1; pos--) {
for (int i=0; i<starting_base; i++) {
if (hex[i] == value[pos]) {
return_value+=i*pow((float)starting_base, digit++);
break;
}
}
}
return return_value;
}
char* from_dec(int value, int ending_base) {
char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char *return_value = (char *)malloc(30);
unsigned short int digit = (int)ceil(log10((double)(value+1))/log10((double)ending_base));
return_value[digit] = 0;
for (; value != 0; value/=ending_base) return_value[--digit] = hex[value%ending_base];
return return_value;
}
I'm pretty sure this is more advanced than it was meant to be. How do you think I was supposed to do it?
I'm essentially looking for two kinds of answers:
Examples of what a simple solution like the one my teacher probably expected would be.
Suggestions on how to improve the code.
I don't think you need the inner loop:
for (int i=0; i<starting_base; i++) {
What is its purpose?
Rather, you should get the character at value[ pos ] and convert it to an integer. The conversion depends on base, so it may be better to do it in a separate function.
You are defining char hex[ 16 ] twice, once in each function. It may better to do it at only one place.
EDIT 1:
Since this is "homework" tagged, I cannot give you the full answer. However, here is an example of how to_dec() is supposed to work. (Ideally, you should have constructed this!)
Input:
char * value = 3012,
int base = 4,
Math:
Number = 3 * 4^3 + 0 * 4^2 + 1 * 4^1 + 2 * 4^0 = 192 + 0 + 4 + 2 = 198
Expected working of the loop:
x = 0
x = 4x + 3 = 3
x = 4x + 0 = 12
x = 4x + 1 = 49
x = 4x + 2 = 198
return x;
EDIT 2:
Fair enough! So, here is some more :-)
Here is a code sketch. Not compiled or tested though. This is direct translation of the example I provided earlier.
unsigned
to_dec( char * inputString, unsigned base )
{
unsigned rv = 0; // return value
unsigned c; // character converted to integer
for( char * p = inputString; *p; ++p ) // p iterates through the string
{
c = *p - hex[0];
rv = base * rv + c;
}
return rv;
}
I would stay away from GOTO statements unless they are absolutely necessary. GOTO statements are easy to use but will lead to 'spaghetti code'.
Try using a loop instead. Something along the lines of this:
bool base_is_invalid = true;
while ( base_is_invalid ) {
cout << "Enter the number, then starting base, then ending base:" << endl;
cin >> value >> starting_base >> ending_base;
if (starting_base < 2 || starting_base > 16 || ending_base < 2 || ending_base > 16)
cout << "Invalid number. ";
else
base_is_invalid = false;
}
You can initialize arrays by string literals (notice that the terminating \0 is not included because the size of the array doesn't permit that):
char const hex[16] = "0123456789ABCDEF";
Or just use a pointer to the string literal for the same effect:
char const* hex = "0123456789ABCDEF";
to_dec() looks to complicated, here is my shot at it:
int to_dec(char* value, int starting_base)
{
int return_value = 0;
for (char* cur = value + strlen(value) - 1; cur >= value; cur--) {
// assuming chars are ascii/utf: 0-9=48-57, A-F=65-70
// faster than loop
int inval = *cur - 48;
if (inval > 9) {
inval = *cur - 55;
if (inval > 15) {
// throw input error
}
}
if (inval < 0) {
// throw input error
}
if (inval >= starting_base) {
// throw input error
}
// now the simple calc
return_value *= starting_base;
return_value += inval;
}
return return_value;
}
for the initial conversion from ascii to an integer, you can also use a lookup table (just as you are using a lookuptable to to the conversion the other way around) , which is much faster then searching through the array for every digit.
int to_dec(char value[], int starting_base)
{
char asc2BaseTab = {0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,-1,10,11,12,13,14,15, //0-9 and A-F (big caps)
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //unused ascii chars
10,11,12,13,14,15}; //a-f (small caps)
srcIdx = strlen(value);
int number=0;
while((--srcIdx) >= 0)
{
number *= starting_base;
char asciiDigit = value[srcIdx];
if(asciiDigit<'0' || asciiDigit>'f')
{
//display input error
}
char digit = asc2BaseTab[asciiDigit - '0'];
if(digit == -1)
{
//display input error
}
number += digit;
}
return number;
}
p.s. excuses if there are some compile errors in this...I couldn't test it...but the logic is sound.
In your description of the assignment as given it says:
"I was asked to use separate functions to convert to and from base 10."
If that is really what the teacher meant and wanted, which is doubtful, your code doesn't do that:
int to_dec(char value[], int starting_base)
is returning an int which is a binary number. :-) Which in my opinion does make more sense.
Did the teacher even notice that?
C and C++ are different languages, and with different styles of programming. You better not to mix them. (Where C and C++ differ)
If you are trying to use C++, then:
Use std::string instead of char* or char[].
int to_dec(string value, int starting_base);
string from_dec(int value, int ending_base);
No any mallocs, use new/delete. But actually C++ manages memory automatically. The memory is freed as soon as variable is out of scope (unless you are dealing with pointers). And pointers are the last thing you need to deal with.
We don't need here any lookup tables, just a magic string.
string hex = "0123456789ABCDEF";//The index of the letter is its decimal value. A is 10, F is 15.
//usage
char c = 'B';
int value = hex.find( c );//works only with uppercase;
The refactored to_dec can be like that.
int to_dec(string value, int starting_base) {
string hex = "0123456789ABCDEF";
int result = 0;
for (int power = 0; power < value.size(); ++power) {
result += hex.find( value.at(value.size()-power-1) ) * pow((float)starting_base, power);
}
return result;
}
And there is a more elegant algorithm to convert from base 10 to any other
See there for example. You have the opportunity to code it yourself :)
In your from_dec function, you're converting the digits from left to right. An alternative is to convert from right to left. That is,
std::string from_dec(int n, int base)
{
std::string result;
bool is_negative = n < 0;
if (is_negative)
{
n = - n;
}
while (n != 0)
{
result = DIGITS[n % base] + result;
n /= base;
}
if (is_negative)
{
result = '-' + result;
}
return result;
}
This way, you won't need the log function.
(BTW, to_dec and from_dec are inaccurate names. Your computer doesn't store numbers in base 10.)
Got this question on an interview once and brainfarted and spun wheels for a while. Go figure. Anyway, a couple years later I'm going through Math and Physics for Programmers to brush up for positions that are more math intensive than what I've been doing. CH1 "assignment" has
// Write a function ConvertBase(Number, Base1, Base2) which takes a
// string or array representing an integer in Base1 and converts it
// into base Base2, returning the new string.
So, I took an approach mentioned above: I convert string in arbitrary base to UINT64, then I convert UINT64 back to arbitrary base:
CString ConvertBase(const CString& strNumber, int base1, int base2)
{
return ValueToBaseString(BaseStringToValue(strNumber, base1), base2);
}
Each of the subfunctions has a recursive solution. Here's one for example:
UINT64 BaseStringToValue(const CString& strNumber, int base)
{
if (strNumber.IsEmpty())
{
return 0;
}
CString outDigit = strNumber.Right(1);
UINT64 output = DigitToInt(outDigit[0]);
CString strRemaining = strNumber.Left(strNumber.GetLength() - 1);
UINT64 val = BaseStringToValue(strRemaining, base);
output += val * base;
return output;
}
I find the other one slightly harder to grasp mentally, but it works roughly the same way.
I also implemented DigitToInt and IntToDigit which work just like they sound. You can take some neat shortcuts there, by the way, if you realize that chars are ints then you don't need huge switch statements:
int DigitToInt(wchar_t cDigit)
{
cDigit = toupper(cDigit);
if (cDigit >= '0' && cDigit <= '9')
{
return cDigit - '0';
}
return cDigit - 'A' + 10;
}
and unit tests are really your friend here:
typedef struct
{
CString number;
int base1;
int base2;
CString answer;
} Input;
Input input[] =
{
{ "345678", 10, 16, "5464E"},
{ "FAE211", 16, 8, "76561021" },
{ "FAE211", 16, 2, "111110101110001000010001"},
{ "110110111", 2, 10, "439" }
};
(snip)
for (int i = 0 ; i < sizeof(input) / sizeof(input[0]) ; i++)
{
CString result = ConvertBase(input[i].number, input[i].base1, input[i].base2);
printf("%S in base %d is %S in base %d (%S expected - %s)\n", (const WCHAR*)input[i].number,
input[i].base1,
(const WCHAR*) result,
input[i].base2,
(const WCHAR*) input[i].answer,
result == input[i].answer ? "CORRECT" : "WRONG");
}
And here's the output:
345678 in base 10 is 5464E in base 16 (5464E expected - CORRECT)
FAE211 in base 16 is 76561021 in base 8 (76561021 expected - CORRECT)
FAE211 in base 16 is 111110101110001000010001 in base 2 (111110101110001000010001 expected - CORRECT)
110110111 in base 2 is 439 in base 10 (439 expected - CORRECT)
Now I took some shortcuts in coding by using CString types, etc. I was giving no consideration to efficiency or performance, I just wanted to solve the algorithm with easiest coding possible.
It can help to understand how these algorithms are recursive if you write them like so: Say you want to determine the "value" of the "string" B4A3, which is in base 13. You know it's 3 + 13(A) + 13(13)(4) + 13(13)(13)(B) Another way to write that is: 0+3+13(A+13(4+13(B))) - and voila! Recursion.
Apart from the things already mentioned, I would suggest using the new-operator instead of free. The advantages of new are that it also does call constructors - which is irrelevant here since you're using a POD type, but important when it comes to objects such as std::string or your own custom classes - and that you can overload the new operator to suit your specific needs (which is irrelevant here, too :p). But don't go ahead using malloc for PODs and new for classes, since mixing them is considered bad style.
But okay, you got yourself some heap memory in from_dec... but where is it freed again? Basic rule: memory that you malloc (or calloc etc) must be passed to free at some point. The same rule applies to the new-operator, just that the release-operator is called delete. Note that for arrays, you need new[] and delete[]. DON'T ever allocate with new and release with delete[] or the other way around, since the memory won't be released correctly.
Nothing evil will happen when your toy program won't release the memory... I guess your PC has got enough RAM to cope with it and when you shut down your program, the OS releases the memory anyway.. but not all programs are (a) that tiny and (b) shut down often.
Also I'd avoid conio.h, since this is not portable. You're not using the most complicated IO, so the standard headers (iostream etc) should do.
Likewise, I think most programmers using modern languages follow the rule "Only use goto if other solutions are really crippled or tons of more work". This is a situation that can be easily solved by using loops, as shown by emceefly. In your program the goto is easy to handle, but you won't be writing such small programs forever, will you? ;)
I, for example, was presented with some legacy code recently.. 2000 lines of goto-littered code, yay! Trying to follow the code's logical flow was almost impossible ("Oh, jump ahead 200 lines, great... who needs context anyway"), even harder was to rewrite the damn thing.
So okay, your goto doesn't hurt here, but where's the benefit? 2-3 lines shorter? Doesn't really matter overall (if you're paid by lines of code, this could also be a major disadvantage ;)). Personally I find the loop version more readable and clean.
As you see, most of the points here can be ignored easily for your program, since it's a toy program. But when you think of larger programs, they make more sense (hopefully) ;)