You are here:

C++/C++ test score drop question

Advertisement


Question
I'm trying to write a program where the user inputs 5 scores and has to find the lowest score. It has to be in two seperate functions (getValues and findLowest). I have the getValue function done alright; I have the user input the 5 scores as an array of type float. When I move on to the second function (findLowest), I don't know how to transfer the scores found in getValues. I can't use global variables, and I think I'm supposed to use call by reference, but I'm confused at how you would take the array from getValues and do a call by reference to get the scores into findLowest. Yikes! Any help is much appreciated!

Answer
First I would like to say how nice it is that someone who has obviously been set some coursework bothers to try it themselves and asks me a specific question relating to a specific problem they are having. Not only that but your question describes in enough detail exactly what your problem is. I hope you do well and go far.

Anyhow, as to the solution to your problem.

First I presume you have something like so (note: you are asking a C++ expert thus I am using C++ style idioms):

   void getValues()
   {
       unsigned int const NumberOfValues(5);
       float values[NumberOfValues];
   
       // code to obtain values form the user

       // hmm... what to do with values array?
   }

You cannot pass the values array out of getValues as an array as this is not allowed and the values array is local to getValues anyway and therefore is destroyed on return from calls to getValues.

You could pass the array out as a pointer - the name of an array will convert to a pointer to the first element of that array. However this is fragile as the caller of getValues does not know how many items are in the array pointed to, and we still have the problem that the array is destroyed on return from the call to getValues.

This latter could be got around by dynamically allocating the values array:

   float * getValues()
   {
       unsigned int const NumberOfValues(5);

       float * values = new float[NumberOfValues];

       // code to obtain values from the user

       return values;
   }

However the caller still has no idea how many items are pointed to by the returned pointer - 1, 2, 5, 100, ?? - and now they will have to remember to delete the array when they are done with it using the delete [] operator. Now maybe getValues does not use the new [] operator to allocate the array - how is a caller of a function (in general) to know how that function is implemented? In such a case some other method of deallocation might be required - maybe getValues used the C malloc function to obtain the memory, in which case the C free function would need to be called to release it when done. All the caller knows is that they got a pointer to a float back.

Finally because normal forms of new and new [] in ISO standard C++ throw a std::bad_alloc exception, getValues can also throw this exception so callers should handle this possibility:

   try
   {
       float * pValues( getValues() );
   }
   catch ( std::bad_alloc & e )
   {
   // handle memory exhaustion
   }

OK so returning an array of values has its problems and complexities, what is the alternative?

The alternative is to pass the array of values into the getValues function in the first place. That puts both the allocation and deallocation requirements on the caller and the caller knows in advance how many items are in the array.

Now you are worried about pass by value and pass by reference. In the case of C and C++ built in arrays this is not a problem - they are effectively always passed by reference as it is the pointer to the first array element that is passed to functions taking arrays. Note the C and C++ close relationship between pointers and arrays strikes again! This is useful here but as we shall see in a bit it is not always useful - doh!

This leads to three possible signature variations for getValues, although those with two parameters can of course specify them in the opposite order, and for some error reporting schemes (as mentioned below) a return type other than void might be a required:

1/ We pass in a pointer to the array of values to be filled in, _and_ the number of values we wish obtained:

   void getValues( float * values, unsigned int numberOfValues )
   {
   // code to obtain values from the user
   }

We can use this version like so:

   unsigned int const NumberOfValues(5);
   float values[NumberOfValues];

   getValues( values, NumberOfValues );

One point to note is that because we pass the array as a pointer it is possible for that pointer to be a null pointer value:

   getValues( 0, NumberOfValues );

And your implementation of getValues should therefore check for this possibility and handle it appropriately. Some possible options are (in C++ style preference):

  - throw an exception
  - return an error value - which would mean getValues no longer
    returns void, but say a bool or int value indicating success or
    failure (and in the int case maybe which specific failure).
  - fill the values array with bad float values (such as not a number
    values, commonly called NaN)
  - exit unceremoniously from the program, maybe with an error
    message (not recommended).

There is also the possibility of using the C assert macro which for debug builds (those that do _not_ define an NDEBUG macro) will evaluate an expression and terminate the program in some way if that expression is false. Often the way the program terminates shows exactly where the failed assertion occurred in the code and in some cases can interact with debuggers to allow the programmer to immediately start debugging the problem. Note that for non-debug builds (for which the NDEBUG macro is defined) the assert macro does nothing effectively removing the assertion expression from your code so _never_, _never_ do real work in such expressions! To use the assert macro include <cassert> (or <assert.h> for C) in your code.

I often use asserts in conjunction with throw to give me a change to debug at the problem site before the context is lost through the throw mechanism:

   assert(0!=values);
   if ( 0==values )
   {
       throw std::invalid_argument
         ("Null values array passed to getValues");
   }

2/ We pass an array of 5 floats to the function:

   unsigned int const NumberOfValues(5);

   // ...

   void getValues( float values[NumberOfValues] )
   {
   // code to obtain values from the user
   }

This looks promising. However this does not help much as all function parameters of array-of-type are 'adjusted' (the ISO C and C++ standards' term) to pointer-to-type. Thus the above devolves to the previous example, but without any help from the caller on how many elements actually are in the array passed. In fact I tried calling the above version with the correct size array, arrays of 4 and 6 floats and a null pointer:

   float values[NumberOfValues];
   float v4[4];
   float v6[6];

   getValues( values );  // OK, what we want

   getValues( v4 );      // OK, eh?
   getValues( v6 );      // OK, eh?
   getValues( 0 );       // OK, eh?

I then compiled the code using two compilers (Microsoft Visual C++ 2005 and GNU g++ 4.1.3) using maximum warning levels and not a peep out of either of them for the three lines that we might expect a problem with.

3/ In a variation of the previous two, we use an unsized array type together with a number of elements indicator:

   void getValues( float values[], unsigned int numberOfValues )

This is pretty much equivalent to the first signature, including how to use it, and of course with all the ifs and buts.

OK so that's the case for getValues - pass in the array from wherever getValues is called using variations on signature forms 1 or 3 above.

Of course similar arguments apply to findLowest. However I presume that in this case a single float should be returned:

   float findLowest(float * values, unsigned int numberOfValues)
   {
   // ...
   }

Or:
   float findLowest(float values[], unsigned int numberOfValues)
   {
   // ...
   }

Your main code would then look something like (less error checking):

   unsigned int const NumberOfValues(5);
   float values[NumberOfValues];

   getValues( values, NumberOfValues );

   // check values obtained OK

   std::cout << "The Lowest value is "
         << findLowest( values, NumberOfValues )
         << '\n'
         ;

Hope this helps.  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.