How it could be optimized [C++]? - c++

I was doing some C++ "homework" and I've made an exercise which says the following:
Input
The input consists of a sequence of characters, which include exclusively lowercase letters, spaces and newlines.
[Table with letter-number values]
Output
Your program must print the total value of the message, computed as the sum of the value of all its letters.
And I've done this!:
#include<iostream>
using namespace std;
int main() {
char v;
int val = 0;
while(cin >> v){
if (v == 'a' or v == 'e') val += 1;
if (v == 'o' or v == 's') val += 2;
if (v == 'd' or v == 'i' or v == 'n' or v == 'r') val += 3;
if (v == 'c' or v == 'l' or v == 't' or v == 'u') val += 4;
if (v == 'm' or v == 'p') val += 5;
if (v == 'k' or v == 'w') val += 7;
if (v == 'b' or v == 'f' or v == 'g' or v == 'h') val += 6;
if (v == 'j' or v == 'q' or v == 'v' or v == 'x' or v == 'y' or v == 'z') val += 6;
}
cout << val << endl;
}
Example:
INPUT:
is a chinese wok
OUTPUT:
42
(I end the While loop by pressing Ctrl+D, which is how the evaluation web does.)
Which is a pretty simple and working solution but...
I was wandering if there's any way of doing this without a bunch of "if's". Gotta say, I can't include anything else than iostream.
Thanks!

As another answer indicates, you can use a map. That's the most compact way of writing this algorithm, but it will not necessarily result in best performance.
There are two more ways that I can think of. A somewhat better way is with a switch statement, like this:
int get_increment( char v )
{
switch( v )
{
case 'a': case 'e':
return 1;
case 'o': case 's':
return 2;
case 'd': case 'i': case 'n': case 'r':
return 3;
case 'c': case 'l': case 't': case 'u':
return 4;
case 'm': case 'p':
return 5;
case 'k': case 'b': case 'f': case 'g': case 'h':
return 6;
case 'w':
return 7;
case 'j': case 'q': case 'v': case 'x': case 'y': case 'z':
return 6;
default:
return 0; //not a letter!
}
}
but if you want the maximum performance, then a lookup table is the way to go.
Here is how to initialize the lookup table:
int increments[256];
for( int i = 0; i < 256; i++ )
increments[i] = 0;
for( char c = 'a'; c <= 'z'; c++ )
increments[c] = get_increment( c );
and then here is how to use it:
val += increments[(unsigned char)v];
Note: the cast of v to unsigned char is not strictly speaking necessary, if v is only going to contain letters. But it will save your program from a crash if characters in your architecture are signed, (and they usually are,) and v happens to contain a negative value.

Related

Stack smashing detected

#include <iostream>
using namespace std;
int main()
{
int tablica[9];
string inputromanum;
cout << "ROMAN: ";
cin >> inputromanum;
int maxindeks;
bool disablenextcomp = false;
int readysolution = 0;
maxindeks = inputromanum.length() - 1;{}{}
for (int i = 0; i <= maxindeks; i++)
{
if (inputromanum[i] == 'M' || inputromanum[i] == 'm')
{
tablica[i] = 1000;
}
if (inputromanum[i] == 'D' || inputromanum[i] == 'd')
{
tablica[i] = 500;
}
if (inputromanum[i] == 'C'|| inputromanum[i] == 'c')
{
tablica[i] = 100;
}
if (inputromanum[i] == 'L' || inputromanum[i] == 'l')
{
tablica[i] = 50;
}
if (inputromanum[i] == 'X' || inputromanum[i] == 'x')
{
tablica[i] = 10;
}
if (inputromanum[i] == 'V' || inputromanum[i] == 'v')
{
tablica[i] = 5;
}
if (inputromanum[i] == 'I' || inputromanum[i] == 'i')
{
tablica[i] = 1;
}
}
cout<<endl;
for(int i4 = 0; i4 <= maxindeks; i4++)
{
cout<<"tablica["<<i4<<"] = "<<tablica[i4]<<endl;
}
for (int i2 = 0; i2 <= maxindeks; i2++)
{
int i5 = i2 + 1;
if (i5 <= maxindeks)
{
//cout<<endl<<"tablica[i2 + 1] = "<<tablica[i2 + 1];
//cout<<endl<<"tablica[i2] = "<<tablica[i2];
//cout<<endl<<"tablica[i2 + 1] - tablica[i2] = "<<tablica[i2 + 1] - tablica[i2];
if (tablica[i2 + 1] - tablica[i2] > 0 && disablenextcomp == false)
{
//cout<<endl<<"readysolution + (tablica[i2 + 1] - tablica[i2]) = "<<readysolution + (tablica[i2 + 1] - tablica[i2])<<endl;
readysolution = readysolution + (tablica[i2 + 1] - tablica[i2]);
disablenextcomp = true;
}
else
{
if(disablenextcomp == false)
{
//cout<<endl<<"readysolution + tablica[i2] = "<<readysolution + tablica[i2]<<endl;
readysolution = readysolution + tablica[i2];
}
else
{
disablenextcomp = false;
}
}
}
else
{
if(disablenextcomp == false)
{
//cout<<endl<<endl<<"OSTATNI INDEKS";
//cout<<endl<<"tablica[i2] = "<<tablica[i2];
//cout<<endl<<"readysolution + tablica[i2] = "<<readysolution + tablica[i2];
readysolution = readysolution + tablica[i2];
}
}
i5++;
}
cout << endl << readysolution;
}
This is my program. made for decoding roman numerals into arabic ones. It works as intended in most cases, however, one of my colleagues found it to produce this error while inputting MMMCMXCVIII into the program:
*** stack smashing detected ***: terminated
It would refuse to work afterwards.
I wasn't able to find different numbers that would cause this error except MMMMMMMMMMM.
It seems to fail when the index of tablica array exceeds 10. I don't know why it does so, as i am a novice in c++. It should've outputted 3999 instead of the error appearing. The numbers it should process successfully should range from 1 to 5000.
Thanks to folks in the comments, I've found the cause.
The tablica[9] array is supposed to store 9 or less characters.
The length of the input (MMMCMXCVIII in this case) has more characters, therefore it makes the for loop responsible for storing values for each character to cause mentioned above error, as there are no remaining units to store the values in.
I've expanded the storage of tablica to 25 characters.
In modern C++ it is considered bad practice to use C-style arrays and index loops whenever you can avoid this. So, fo example you can rewrite first loop like this:
std::vector<int> tablica;
tablica.reserve(inputromanum.size()); // This line is not necessary, but it can help optimize memory allocations
for (char c : inputromanum)
{
if (c == 'M' || c == 'm')
{
tablica.push_back(1000);
}
if (c == 'D' || c == 'd')
{
tablica.push_back(500);
}
if (c == 'C'|| c == 'c')
{
tablica.push_back(100);
}
if (c == 'L' || c == 'l')
{
tablica.push_back(50);
}
if (c == 'X' || c == 'x')
{
tablica.push_back(10);
}
if (c == 'V' || c == 'v')
{
tablica.push_back(5);
}
if (c == 'I' || c == 'i')
{
tablica.push_back(1);
}
}
And you will avoid your issue completly. Something similar can be done with other loops too. This approach also has benefit of (somewhat) properly handling situations when input line has other symbols, which is not roman number. Try it on your version and you will see what I mean.
One more point. When you need to do something different depending of value of one variable, like you did with all those ifs. There is special statement in C/C++ for this: switch. So instead of those ifs you can do this:
std::vector<int> tablica;
tablica.reserve(inputromanum.size()); // This line is not necessary, but it can help optimize memory allocations
for (char c : inputromanum)
{
switch(c)
{
case 'M':
case 'm':
tablica.push_back(1000);
break;
case 'D':
case 'd':
tablica.push_back(500);
break;
case 'C':
case 'c':
tablica.push_back(100);
break;
case 'L':
case 'l':
tablica.push_back(50);
break;
case 'X':
case 'x':
tablica.push_back(10);
break;
case 'V':
case 'v':
tablica.push_back(5);
break;
case 'I':
case 'i':
tablica.push_back(1);
break;
}
}

Am I using this switch case incorrectly?

So the idea of this program is to get the users input in the form of a Roman Numeral up to 4999. I decided to use a switch case to loop through an string of input. The problem is when I put in a value like 99 which would be XCIX it returns -101. Any help is grateful.
int number = 0, M = 1000, D = 500, C = 100, L = 50, X = 10, V = 5, I = 1;
for (int i = 0; i < roman.length(); i++)
{
switch (roman[i])
{
case 'M': number += 1000; break;
case 'D': if (roman[i + 1] != 'D' && i + 1 < roman.size())
number -= 500;
else
number += 500;
break;
case 'C': if (roman[i + 1] != 'C' && i + 1 < roman.size())
number -= 100;
else
number += 100;
break;
case 'L': if (roman[i + 1] != 'L' && i + 1 < roman.size())
number -= 50;
else
number += 50;
break;
case 'X': if (roman[i + 1] != 'X' && i + 1 < roman.size())
number -= 10;
else
number += 10;
break;
case 'V': if (roman[i + 1] != 'V' && i + 1 < roman.size())
number -= 5;
else
number += 5;
break;
case 'I':
if (roman[i + 1] != 'I' && i + 1 < roman.size())
number -= 1;
else
number += 1;
break;
}
}
return number;
Roman numerals count negative only when they appear before a digit with higher value. Being different is not enough.

Set BSTR to null

In a Visual C++ i have a function like shown below.it is a dll code. From vb6 application i am calling dll and run the function and get result.But every time when i call this from my vb6 application(without closing vb6 exe) i get result with appended values of last result if exe is not closed.(ex when i run first result is "a" for second time it is "aa" and so on).So i am using i think it is due to BSTR Message not being set to NULL in the beginning of this function.
Below is code in VC++ . So how to set BSTR to null or empty in the beginning or how to solve my error?
BSTR __stdcall getHardDriveComputerID (short* disk_cnt , short* method)
{
BSTR Message=NULL;
int i,length;
size_t len = 0;
int done = FALSE;
__int64 id = 0;
OSVERSIONINFO version;
strcpy (HardDriveSerialNumber, "");
memset (&version, 0, sizeof (version));
version.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
GetVersionEx (&version);
if (version.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
// this works under WinNT4 or Win2K if you have admin rights
#ifdef PRINTING_TO_CONSOLE_ALLOWED
printf ("\nTrying to read the drive IDs using physical access with admin rights\n");
#endif
done = ReadPhysicalDriveInNTWithAdminRights ();
*disk_cnt=hard_disk_cnt;
*method=1;
if ( ! done)
{
done = ReadIdeDriveAsScsiDriveInNT ();
*disk_cnt=hard_disk_cnt;
*method=2;
}
if ( ! done)
{done = ReadPhysicalDriveInNTWithZeroRights ();
*disk_cnt=hard_disk_cnt;
*method=3;
if ( ! done)
{ done = ReadPhysicalDriveInNTUsingSmart ();
*disk_cnt=hard_disk_cnt;
*method=4;
}
}
else
{
// this works under Win9X and calls a VXD
int attempt = 0;
// try this up to 10 times to get a hard drive serial number
for (attempt = 0;
attempt < 10 && ! done && 0 == HardDriveSerialNumber [0];
attempt++)
done = ReadDrivePortsInWin9X ();
*disk_cnt=hard_disk_cnt;
*method=5;
}
if (HardDriveSerialNumber [0] > 0)
{
char *p = HardDriveSerialNumber;
WriteConstantString ("HardDriveSerialNumber", HardDriveSerialNumber);
// ignore first 5 characters from western digital hard drives if
// the first four characters are WD-W
if ( ! strncmp (HardDriveSerialNumber, "WD-W", 4))
p += 5;
for ( ; p && *p; p++)
{
if ('-' == *p)
continue;
id *= 10;
switch (*p)
{
case '0': id += 0; break;
case '1': id += 1; break;
case '2': id += 2; break;
case '3': id += 3; break;
case '4': id += 4; break;
case '5': id += 5; break;
case '6': id += 6; break;
case '7': id += 7; break;
case '8': id += 8; break;
case '9': id += 9; break;
case 'a': case 'A': id += 10; break;
case 'b': case 'B': id += 11; break;
case 'c': case 'C': id += 12; break;
case 'd': case 'D': id += 13; break;
case 'e': case 'E': id += 14; break;
case 'f': case 'F': id += 15; break;
case 'g': case 'G': id += 16; break;
case 'h': case 'H': id += 17; break;
case 'i': case 'I': id += 18; break;
case 'j': case 'J': id += 19; break;
case 'k': case 'K': id += 20; break;
case 'l': case 'L': id += 21; break;
case 'm': case 'M': id += 22; break;
case 'n': case 'N': id += 23; break;
case 'o': case 'O': id += 24; break;
case 'p': case 'P': id += 25; break;
case 'q': case 'Q': id += 26; break;
case 'r': case 'R': id += 27; break;
case 's': case 'S': id += 28; break;
case 't': case 'T': id += 29; break;
case 'u': case 'U': id += 30; break;
case 'v': case 'V': id += 31; break;
case 'w': case 'W': id += 32; break;
case 'x': case 'X': id += 33; break;
case 'y': case 'Y': id += 34; break;
case 'z': case 'Z': id += 35; break;
}
}
}
id %= 100000000;
if (strstr (HardDriveModelNumber, "IBM-"))
id += 300000000;
else if (strstr (HardDriveModelNumber, "MAXTOR") ||
strstr (HardDriveModelNumber, "Maxtor"))
id += 400000000;
else if (strstr (HardDriveModelNumber, "WDC "))
id += 500000000;
else
id += 600000000;
#ifdef PRINTING_TO_CONSOLE_ALLOWED
printf ("\nHard Drive Serial Number__________: %s\n",
HardDriveSerialNumber);
printf ("\nHard Drive Model Number___________: %s\n",
HardDriveModelNumber);
printf ("\nComputer ID_______________________: %I64d\n", id);
#endif
char tempdisk_serials[]="";
for(i = 0 ; i<=hard_disk_cnt ; i++)
{
if(hard_disk_serial[i] != "")
{
strcat(tempdisk_serials, hard_disk_serial[i]);
length = strlen(tempdisk_serials);
/* Check if we need to insert newline */
if(tempdisk_serials[length-1] != '\n')
{
tempdisk_serials[length] = '\n'; /* Append a newline */
tempdisk_serials[length+1] = '\0'; /* followed by terminator */
}
}
}
//Converting char[] to BSTR
Message = SysAllocStringByteLen (tempdisk_serials, strlen(tempdisk_serials));
return (BSTR) Message;
}
This is modified code of shown in link http://www.winsim.com/diskid32/diskid32.cpp
EDIT
Its not problem with tempdisk_serials actually .This variable is set from hard_disk_serial. It is declared globally as char hard_disk_serial[16][1024];(not shown in code in my question) . How to clear this variable in my function? i tried hard_disk_serial[][]={0}; and it gives syntax error.
To clear buffer, use memset function doc
memset (hard_disk_serial, 0, 1024*16);

Convert If/Else's into a switch somehow

So basically what I'm trying to do it turn this chunk of code into a switch statement somehow. I'm not really sure how to do this. I had a couple of ideas like making an enum with keywords that had a range of values (ie "enum hare { ONE =(range from 1-5) TWO = (range from 6-8)} - But i dont know if that is an error :S
Hopefully I've been clear in what I'm trying to ask.
int y = 1 + rand() % 10;
// determine which move to make
if ( y == 1 || y == 2 ) {
hare += 0;
} else if ( y == 3 || y == 4 ) {
hare += 9;
} else if (y == 5) {
hare -= 12;
} else if (y >= 6 && y <= 8){
hare += 1;
} else if (y == 9 || y == 10){
hare -= 2;
}else {
++( hare );
}
if ( hare < 1 ) {
hare = 1;
} else if ( hare > RACE_END ) {
hare = RACE_END;
}
The first question is whether there is real value in doing the conversion. Then the operation is actually simple for this case, since in all cases except the else there is a small number of valid cases:
switch (y) {
case 1: case 2:
break;
case 3: case 4:
hare += 9;
break;
case 5:
hare -= 12;
break;
case 6: case 7: case 8:
hare += 1;
break;
case 9: case 10:
hare -= 2;
break;
default:
++hare;
}
If you want to use switch statement, you can do like this
switch( y ){
case 1:
case 2: hare += 0; break;
case 3:
case 4: hare += 9; break;
case 5: hare -= 12; break
case 6:
case 7:
case 8: hare += 1; break;
case 9:
case 10: hare -= 2; break;
default: ++(hare);
}
But, using enum, enum hare { ONE =(range from 1-5) TWO = (range from 6-8)} is that ... you are trying to store multiple values in one variable, which is not possible.
Reference for switch statement
You can easily do something like this:
switch(y)
{
case 1:
case 2:
//do something
break;
default:
//
}
Therefore falling through your case conditions according the values of y.
int y = 1 + rand() % 10;
switch ( y )
{
case 1:
case 2:
hare += 0;
break;
case 3:
case 4:
hare+= 9;
break;
case 5:
hare -= 12;
break;
case 6:
case 7:
case 8:
hare += 1;
break;
case 9:
case 10:
hare -= 2;
break;
default:
++hare;
break;
}
if ( hare < 1 ) {
hare = 1;
} else if ( hare > RACE_END ) {
hare = RACE_END;
}
You can do it like this.
int y = 1 + rand() % 10;
switch(y)
{
case 1: //1 or 2
case 2:
hare += 0;
break;
case 3: //3 or 4
case 4:
hare += 9;
break;
case 5:
hare -= 12;
break;
case 6:
case 7:
case 8:
hare += 1;
break;
case 9:
case 10:
hare -= 2;
break
default:
if ( hare < 1 ) {
hare = 1;
} else if ( hare > RACE_END ) {
hare = RACE_END;
}
else
++( hare );
break;
}

C++ program not distinguishing between upper and lower case letters in OpenCV application

I'm editing a relatively big OpenCV-based program written by some colleagues.
The problem I'm facing is that the program does not distinguish between upper and lower case keystrokes (i.e both keystrokes 'd' and 'D' are seen as 0x65)
My colleague told me that he solved the problem on his machine by compiling the OpenCV libraries on his machine directly from source.
I'm a newbie, but also curious why such thing would even happen and how would it be related to OpenCV installation!
I still have the problem on my machine but I have not tried reinstalling openCV.
EDIT:
OS Linux-Ubuntu 12.10 64bit
The letters are keystrokes from the Keyboard and they are used in a switch statement.
static int process_key(struct cam_segment* cs, int key){
int res = 0;
double pdist;
struct stat sb;
char fn[4096];
static int out_id = 0;
fprintf( stderr, "%d\n", key&0xff );
switch (key & 0xff) {
case 'm':
show_merged = !show_merged;
res = 1;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
cam_id = (key & 0xff) - '1';
res = 1;
break;
case 'D':
ocv_mgrid_set_distortion( cs->mg, 0 );
res = 1;
break;
case 'd':
ocv_mgrid_set_distortion( cs->mg, 1 );
res = 1;
break;
case 'n':
res = 1;
break;
case 'C':
init_calibration(cs);
calibrate_camera_distortion(cs->mg);
output_calibration_results(cs);
cs->calib_mode = 1;
res = 1;
break;
case 'c':
init_calibration(cs);
fprintf(stderr,"searching.....");
calibrate_camera_rotation(cs->mg);
output_calibration_results(cs);
fprintf(stderr,"e1 %f\n",water_error(cs->mg));
cs->calib_mode = 1;
res = 1;
break;
case 'M':
init_calibration(cs);
fprintf(stderr,"searching pricipal point.....");
calibrate_camera_center(cs->mg);
output_calibration_results(cs);
cs->calib_mode = 1;
res = 1;
break;
case 'a':
init_calibration(cs);
int misscnt;
double e,en;
en = e = HUGE;
misscnt = 0;
for(int i=0;i<20;i++) {
e = water_error(cs->mg);
fprintf(stderr,"e1 %f %d \n",e,misscnt);
calibrate_camera_rotation(cs->mg);
output_calibration_results(cs);
en = water_error(cs->mg);
fprintf(stderr,"en1 %f\n",en);
if (en >= e)
misscnt++;
else
misscnt = 0;
if (misscnt > 3)
break;
e = water_error(cs->mg);
fprintf(stderr,"e %f %d\n",e,misscnt);
calibrate_coupled_focal_length(cs->mg);
output_calibration_results(cs);
en = water_error(cs->mg);
fprintf(stderr,"en %f\n",en);
if (en >= e)
misscnt++;
else
misscnt = 0;
if (misscnt > 3)
break;
e = water_error(cs->mg);
if (e > 100)
continue;
fprintf(stderr,"e %f %d\n",e,misscnt);
calibrate_camera_distortion(cs->mg);
output_calibration_results(cs);
en = water_error(cs->mg);
fprintf(stderr,"en %f\n",en);
if (en >= e)
misscnt++;
else
misscnt = 0;
if (misscnt > 3)
break;
}
cs->calib_mode = 1;
res = 1;
break;
case 'p':
init_calibration(cs);
calibrate_coupled_focal_length(cs->mg);
output_calibration_results(cs);
cs->calib_mode = 1;
res = 1;
break;
case 'F':
init_calibration(cs);
calibrate_focal_lengths(cs->mg);
output_calibration_results(cs);
cs->calib_mode = 1;
res = 1;
break;
case 't':
init_calibration(cs);
calibrate_target(cs->mg);
output_calibration_results(cs);
cs->calib_mode = 1;
res = 1;
break;
case 'e':
sprintf(fn, "%s_%03d.png",cs->inp_prefix, cs->inp_id);
if (!(input = cvLoadImage(fn, CV_LOAD_IMAGE_COLOR))) {
fprintf(stderr,"error calc: loading input failed\n");
} else if (!(ocv_mgrid_set_input(cs->mg, input))) {
fprintf(stderr,"error calc error 1\n");
}
pdist = water_error(cs->mg);
fprintf(stderr,"watererror: %f\n",pdist);
break;
case 'N':
res = 1;
cs->inp_id++;
cerr << "inp_id=" << cs->inp_id << "\n";
break;
case 'P':
res = 1;
cs->inp_id--;
cerr << "inp_id=" << cs->inp_id << "\n";
break;
case 'z':
cs->color_mode = OCV_MGRID_CMODE_THETA;
res = 1;
break;
case 'Z':
cs->color_mode = OCV_MGRID_CMODE_THETA_CHECK;
res = 1;
break;
case 'l':
cs->draw_legend = !(cs->draw_legend);
res = 1;
break;
case 'x':
cs->color_mode = OCV_MGRID_CMODE_X;
res = 1;
break;
case 'y':
cs->color_mode = OCV_MGRID_CMODE_Y;
res = 1;
break;
case 'X':
cs->color_mode = OCV_MGRID_CMODE_X_CHECK;
res = 1;
break;
case 'Y':
cs->color_mode = OCV_MGRID_CMODE_Y_CHECK;
res = 1;
break;
case 'o':
cs->color_mode = OCV_MGRID_CMODE_NONE;
res = 1;
break;
case 's':
sprintf(fn, "input_%03d.png",out_id);
while(stat(fn, &sb) != -1) {
out_id++;
sprintf(fn, "input_%03d.png",out_id);
}
fprintf(stderr,"saving %s \n",fn);
cvSaveImage(fn,OCV_MGRID_GET_INPUT(cs->mg));
break;
case 'r':
if (projection)
cvResetImageROI(projection);
if (merged_projection)
cvResetImageROI(merged_projection);
if (merged_grid)
cvResetImageROI(merged_grid);
if (output)
cvResetImageROI(output);
break;
case 'R':
reset_defaults();
reset_trackbars();
break;
case 'f':
ocv_mgrid_get_best_projection_distance(cs->mg, &pdist);
fprintf(stderr,"best match %f\n",pdist);
tb_pdist = (int) pdist;
cvSetTrackbarPos("proj_distance", "tbars", tb_pdist);
res = 1;
break;
case 'w':
if (use_camera)
if (!(ocv_ueye_whitebalance(cs->ueye)))
return 1;
break;
case 'W':
save_params(cs);
break;
default:
break;
}
return res;
}
I have no idea why until now!
But i have removed the old OpenCV libraries and compiled the latest version. And now it does distinguish between the upper and lower case letters.
The new version has some subtle differences which are not necessarily good, for example values of track bars cannot be edited by clicking on them anymore. In addition (# kebs) the key strokes have to be masked now with 0xff now!
I don't know maybe i had a weird OpenCV version all along!