Originally Posted by
Ionut Achim
...
Zub, first I used numbers to select options, however, somehow if the input required is a number and the user enters a char, at some specific letters (don't remember witch of them), the program will run (Am i wrong if I say that is because of the ASCII code of the letters?).
I guess that depends on the format specifiers you are using when inputting an option ("%c" or "%d") and the data-type of the option (int or char).
Anyway, zub's suggestion for hashing a string to an int (I guess for efficiency reasons later on in the program) which in your case can be done with perfect-hashing (as also suggested by zub), is a nice option imho, because it will open possibilities for improving your code.
If you want to give another chance in reading ints instead of strings, then the reverse kind of the suggested hashing (int to string) may also prove useful in the long run. With a little extra code for hard-coding the hash-table and a couple of supporting code (an enum and a validation macro) you can have something like this:
Code:
#include <stdio.h>
#include <stdlib.h>
#define VALID_UNIT(u) ( (u) > UNIT_NONE && (u) < MAX_UNITS )
enum {
UNIT_NONE = -1,
UNIT_WH = 0,
UNIT_KWH,
UNIT_MWH,
/* not a unit, just their total count */
MAX_UNITS
};
...
int main( void )
{
int unit = UNIT_NONE;
char *labels[MAX_UNITS] = { "wh", "kwh", "mwh" };
...
You can then use the enumerated values for indexing the labels array and getting the corresponding string, if for some reason you want to handle units as strings instead of int... e.g. for printing them in human readable form.
In such a case, you would input the unit variable from the user, validate it, and then print the label that corresponds to that unit...
Code:
printf( "Choose unit: " );
if ( 1 == scanf("%d", &unit) && VALID_UNIT(unit) ) {
printf( "you chose \"%s\"\n", labels[unit] );
}
Actually, the enumerated values may now be used for indexing any array related to your units. For your example, you could define a multipliers array, in parallel with the labels array... something like this:
Code:
...
int main( void )
{
int unit = UNIT_NONE;
char *labels[MAX_UNITS] = { "wh", "kwh", "mwh" };
double multipliers[MAX_UNITS] = { 1.0, 1000.0, 1000000.0 };
...
so that when the user inputted unit var has been successfully validated, along with the whour var, you can calculate your result directly:
Code:
...
result = 3600.0 * ( whour * multipliers[unit] );
...
If you are up to it, you could also warp the labels & multipliers arrays into a struct, or if you are not very fond of parallel-arrays, you could define a struct with a string and a double field, and then define an array of that struct.
Parallels arrays are just fine for getting you started though, imho.
On another note, regarding fflush( stdin ). As already pointed out by Elkvis, this happens to work on Windows, but according to the C standards it produces undefined behavior. If you try it on other platforms, you'll see that on most of them it is not working.
It has to do with the buffered nature of stdin, and a somewhat quick & dirty solution (although not full proff) is to use fgets() along with sscanf() when reading from stdin, using a custom buffer in between.
For example...
Code:
#include <stdio.h> /* for BUFSIZ among other things */
int main( void )
{
char input[BUFSIZ] = {'\0'};
double d = .0;
if ( NULL == fgets( input, BUSIZ, stdin )
|| 1 != sscanf( input, "%lf", &d )
){
fputs( "*** error, bye...\n", stderr );
return 1;
}
printf( "You entered: %f\n", d );
return 0;
}
The user can still mess you up if he/she overflows your input buffer in stdin (you can prevent that too, but the code would be even more cumbersome).
So, all in all... the code-snippet you presented in the 1st post, converted to a small program using what I'm trying to demontsrate in this post, would look something like the following...
Code:
#include <stdio.h>
#define VALID_UNIT(u) ( (u) > UNIT_NONE && (u) < MAX_UNITS )
enum {
UNIT_NONE = -1,
UNIT_WH = 0,
UNIT_KWH,
UNIT_MWH,
/* not a unit, just their total count */
MAX_UNITS
};
/* ------------------------------------------
*
* ------------------------------------------
*/
void units_print_menu( char *labels[MAX_UNITS] )
{
int i;
if ( NULL == labels ) {
return;
}
puts( "Choose unit" );
for (i=0; i < MAX_UNITS; i++) {
if ( NULL == labels[i] ) {
fprintf(
stderr,
"\n%s: NULL element found (labels[%d])\n",
__func__,
i
);
return;
}
printf( "%d. %s\n", i, labels[i] );
}
printf( "> " );
fflush( stdout );
}
/* ------------------------------------------
*
* ------------------------------------------
*/
int main( void )
{
char input[BUFSIZ] = {'\0'};
int unit = UNIT_NONE;
double whour = 0;
double result = 0;
char *labels[MAX_UNITS] = { "wh", "kwh", "mwh" };
double multipliers[MAX_UNITS] = { 1.0, 1000.0, 1000000.0 };
for (;;) {
units_print_menu( labels );
if ( NULL == fgets( input, BUFSIZ, stdin )
|| sscanf( input, "%d", &unit ) < 1
|| !VALID_UNIT( unit )
){
puts( "*** invalid unit or error, try again...\n" );
continue;
}
putchar( '\n' );
printf( "Enter the value: " );
if ( NULL == fgets( input, BUFSIZ, stdin)
|| sscanf( input, "%lf", &whour) < 1
){
puts( "*** invalid whour or error, start over...\n" );
continue;
}
result = 3600.0 * ( whour * multipliers[unit] );
break;
}
printf( "%f\n", result );
return 0;
}