C++/Programming
Expert: Ralph McArdell - 10/22/2004
QuestionI have the following program in C++... A user enters a length in INCHES and i convert it to the listed units. I am trying to get it to work for CM first. and it wont... here is what i have so far:
#include <stdio.h>
#define CM=(2.54*inch)
void main(void) {
float inch,cm,mm,m,yrd,mls,km,ft;
printf("\nEnter Length In Inches: ") ;
scanf("%d",&inch);
if (inch<0){
printf("Length CANNOT Be Negative");
}
if (inch>=0){
printf("%d","CM");
}}
AnswerC++ you say. This looks like pure C to me, not C++ - OK so a C++ compiler might compile it but it is still a C program.
Now other than your dodgy layout and use of K & R brace placement - generally not used for readable code these days - your code contains several problems.
So assuming we are talking C here and not C++:
void main( void ) is not a valid signature for main. I would think this is a Microsoft Visual C++ "extension" or deficiency. The only valid forms for main (from the latest C standard) are:
int main(void) { /* ... */ }
and
int main(int argc, char *argv[]) { /* ... */ }
The C++ standard has the equivalent forms:
int main() { /* ... */ }
and
int main(int argc, char* argv[]) { /* ... */ }
The only difference being that in C++ you are allowed to omit the void when specifying an empty argument list for a function.
Now the program does not work for at least two very good reasons.
- first you specify in the patterns to printf and scanf that you are entering / outputting int values, then pass inches - a float type. Use "%f" (float) for scanf and maybe "%6.2f" ( double field 6 characters wide, with 2 decimal places of precision).
- second this call to printf is not what want, even if the output value type were correct:
printf("%d","CM");
The first string contains a pattern specifying that an integer is to be output. This is wrong as mentioned.
You then pass it a string literal - which will be converted to a char * - so you will probably see some strange value that represents the address the string literal exists at in memory...
These are the reason printf, scanf etc can be dangerous - there is no type checking or even checking that there are enough parameters to match the input/output pattern given in the first string. You should probably change the printf statement to something like:
printf("%6.2f", CM);
You should also always check the retuned value from scanf to ensure all fields were assigned to variables.
Now I have had a look at your CM macro and it is horrid! Even for a pre-processor macro. You do work like a function but you hardcode the parameter as a variable named inches. Also the syntax is bad so I doubt the code would even compile. Take out the = .
#define CM (2.54*inch)
A better bet (for a C program) would be to define the inches to cm value:
#define INCHES_TO_CM 2.54
then your printf call would be:
printf("%6.2f", INCHES_TO_CM * inches);
Or to parameterise the macro:
#define INCHES_TO_CM(inches) (inches*2.54)
then
printf("%6.2f", INCHES_TO_CM(inches) );
Now I see no reason to use a vile, unsafe pre-processor macro here unless you are really bothered about efficiency - which you probably are not - so just make INCHES_TO_CM a function and have done with it:
double InchesToCm( double aInches )
{
return 2.54 * aInches;
}
then
printf( "%6.2f", InchesToCm(inches) );
Notice that I changed the type to double - C always passes floating point types as doubles anyway.
Now how about using some actual C++:
// First as we will be using the C++ iostreams, include the appropriate
// C++ library header.
#include <iostream>
// Use an unnamed namespace to keep these values local to this file
namespace
{
// We define the InchesToCm shown previously. As it is very simple
// if we were concerned about performance we could specify it as inline
// as a request to the compiler to expand calls inline at the call
// sites instead of making a function call
double InchesToCm( double aInches )
{
return 2.54 * aInches;
}
}
// Use the correct, C++ style of main we require:
int main()
{
// Use C++ IOStreams to request the inches value
std::cout << "Enter a positive length in inches: ";
// Here I define inches to be double but float would do too
double inches(0.0);
// Use C++ IOStreams to input the value
std::cin >> inches;
if ( inches < 0.0 )
{
// Use C++ IOStreams to output the error message
std::cout << "Value should be positive...\n";
}
if ( inches >= 0.0 )
{
// Use C++ IOStreams to output the result
std::cout << inches << " inches is "
<< InchesToCm(inches) << "cm.\n";
}
}
For both the C and C++ versions of the code the two if statements could be replaced with if .. else as the value is either OK or it is not:
if ( inches < 0.0 )
{
std::cout << "Value should be positive...\n";
}
else // value good
{
std::cout << inches << " inches is " <<
<< InchesToCm(inches) << "cm.\n";
}
Next, you may wish to add a loop to allow the user to enter more than one value for each run. If so you will have to decide how to exit the program.
Now if you would rather have your conversion function return the same type as it is passed - so if you pass an int it returns an int, if you pass a double it returns a double you could just overload the function. I admit this is probably not what you wish for such unit conversions, but it demonstrates some useful C++ features:
int InchesToCm( int aInches )
{
return 2.54 * aInches;
}
double InchesToCm( double aInches )
{
return 2.54 * aInches;
}
The only problem is that you may well receive warnings from the compiler when it converts the double result of 2.54*aInches back to an integer for the int version. If we want to be really flash we could make the function a function template:
namespace
{
template < typename LengthType >
inline LengthType InchesToCm( LengthType aInches )
{
return 2.54 * aInches;
}
}
Note that I made the function inline just for the sake of example.
Then when InchesToCm is called the compiler generates a particular version of InchesToCm from its template definition based on the type of the aInches it is passed - if it is passed a double then a function taking and returning a double is generated. If it is passed an int then a function taking and return an int is generated.
Now I could go on - making the raw values class types that represent units for example - but I think you have enough to think about for now.