What is wrong with this code? (variable argument lists in C++)

Started by
9 comments, last by frob 16 years, 3 months ago
I'm following the code here: http://www.cprogramming.com/tutorial/lesson17.html That code, when copied and pasted, works. However, when I run my own code (which is basically the code from the site, except not CP'ed), I get garbage for output. I've looked my code over and I don't see how it differs from the code on the website except for spacing and variable names. Could someone take a look and see what I'm doing wrong?

#include <iostream>
#include <cstdarg>
using namespace std;

double average(int num, ...)
{
    va_list arguments;    //a place to store the list of arguments
    double sum = 0;

    va_start(arguments, num);   //initializing arguments to store all values after num
    for(int i = 0; i < num; i++)    //loop until all numbers are added
    {
        sum += va_arg(arguments, double);  //adds the next value in argument list to sum
    }

    va_end(arguments);  //cleans up the list

    return sum / num;       //casting ensures accuracy
}

int main()
{
    cout << average(5, 10) << '\n';
    
    return 0;
}
Dat is off da hizzle fo shizzle dizzle
Advertisement
You're passing 5 for the value of num, but you're only supplying one argument after that.
Ohhh. I'm an idiot. Num is the amount of arguments passed to the function. I should have known that. Thanks.
Dat is off da hizzle fo shizzle dizzle
Now that you know how this works, please don't do it any more in C++. There are many limitations and subtle gotchas for a "convenience" that that isn't really.
And now for something completely different.

#include <boost/preprocessor.hpp>#include <cassert>#define AVERAGE_ADD_IMPL(z,n,arg) + arg ## n#define AVERAGE_IMPL(z,n,unused) \      template < typename T > T average( BOOST_PP_ENUM_PARAMS( n, T arg ) ) { \          return ( BOOST_PP_REPEAT( n, AVERAGE_ADD_IMPL, arg ) ) / n; \      }BOOST_PP_REPEAT_FROM_TO( 1, 11, AVERAGE_IMPL, ~ )#undef AVERAGE_IMPL#undef AVERAGE_ADD_IMPLint main() {	assert( average(1,2,3,4,5) == 3 );}


EDIT: Fixed
NOTE: To use, remove whitespaces trailing backslashes added to prevent gdnet from joining all the lines.


And now for something else completely different.

template < typename T, typename Iterator > T average( Iterator begin, Iterator end ) {    unsigned n = 0;    T value = T();    for ( Iterator i = begin ; i != end ; ++i, ++n ) {        value += *i;    }    return value / n;}template < typename T, unsigned N > T average( const T (&array)[N] ) {    return average<T>( array+0, array+N );}int main() {    int values[] = { 1, 2, 3, 4, 5 };    assert( average(values) == 3 );    std::list<int> container;    container.push_back(1);    container.push_back(2);    container.push_back(3);    container.push_back(4);    container.push_back(5);    assert( average<int>( container.begin(), container.end() ) == 3 );}
Quote:Original post by MaulingMonkey
template < typename T, typename Iterator > T average( Iterator begin, Iterator end ) {    unsigned n = 0;    T value = T();    for ( Iterator i = begin ; i != end ; ++i, ++n ) {        value += *i;    }    return value / n;}


Bug: for an arbitrary type, the default constructor is not guaranteed to be the additive identity. Instead, if begin != end, you should copy construct from the first element instead, and start your loop from begin+1.

Bug: if begin == end, you divide by zero. This is undefined behavior. You should detect the condition and do something sane.
Quote:Original post by frob
Bug: if begin == end, you divide by zero. This is undefined behavior. You should detect the condition and do something sane.


Average of zero numbers is undefined per se, and should trigger a division by zero exception.

There's nothing else you can return in this case, at least not with function in this form.
I once wrote some weird template code where you could pass the number of arguments as a template parameter. I cannot, for the life of me, remember how I did that... probably for the best anyway, I'm sure it was full of bugs.
I can't see any weaknesses with something like this compared to the above samples

template<typename T>class Accumulator{	T sum;	int count;public:	Accumulator() : sum(0), count(0) {}	friend Accumulator& operator << (Accumulator& a, T number)	{		a.sum += number;		a.count++;		return a;	}	void reset() { sum = 0; count = 0; }	T average() const { return sum / count; }};#include <iostream>int main(){	Accumulator<double> accumulator;	accumulator << 3.0 << 4.3 << 5.8 << 6.2 << 7.0;	std::cout << "Average: " << accumulator.average() << std::endl;}
Why not std::accumulate?
T average = std::accumulate(begin, end, T()) / std::distance(begin, end);
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]

This topic is closed to new replies.

Advertisement