tags:

views:

479

answers:

5

I understand this code calculates the sum of the args of a variable, however, I don't understand how it works. It looks really abstract to me. Can someone explain how the below works?

Thanks!

#include <stdio.h>

#define sum(...) \
    _sum(sizeof((int []){ __VA_ARGS__ }) / sizeof(int), (int []){ __VA_ARGS__ })

int _sum(size_t count, int values[])
{
    int s = 0;
    while(count--) s += values[count];
    return s;
}

int main(void)
{
    printf("%i", sum(1, 2, 3));
}
+20  A: 

With the pre-processor macro

#define sum(...) \
    _sum(sizeof((int []){ __VA_ARGS__ }) / sizeof(int), (int []){ __VA_ARGS__ })

being called with sum(1,2,3), the line is translated (a simple string substitution, replacing "__VA_ARGS__" with "1,2,3") into:

_sum(sizeof((int []){1,2,3}) / sizeof(int), (int []){1,2,3})

which is a function call to _sum() passing two things:

  • the number of integers in the array {1,2,3} which is 3 (it gets this by dividing the size of the three-integer array by the size of a single integer).
  • the pointer to the array itself (or a totally different array containing the same values, depending on how smart your compiler is).

All the _sum() function does is add each of the integers to s (which is initially zero) until the count runs out.


That first bullet point above bears some explanation. When you have an array of N elements defined as follows:

tType x[22];

the size of the array is sizeof(x), the size of all elements. The size of a single element of that array is sizeof(x[0]), the size of the first element, although I often prefer the sizeof(*x) variant.

So, to count the number of elements, you simply divide the total size by the size of an element, using one of the following:

sizeof(x) / sizeof(x[0])
sizeof(x) / sizeof(*x)

And, since you've asked for a detailed analysis of the code, here we go:

// Needed for printf().

#include <stdio.h>

// Macro to convert sum(n1,n2,...,nN) to _sum(N,n1,n2,...,nN).
// This calculates the length of the array by dividing its size by the size
//   of an int and passes both the length and array through to the new
//   function.
// __VA_ARGS__ is replaced with the entire marcro argument list, '1,2,3' in
//   this case.

#define sum(...) \
    _sum(sizeof((int []){ __VA_ARGS__ }) / sizeof(int), (int []){ __VA_ARGS__ })

// Function to take size and pointer to int array, and return sum.

int _sum (size_t count, int values[]) {
    int s = 0;                // Initial sum of zero.
    while(count--)            // Until elements exhausted (count down).
        s += values[count];   // Add each array element to accumulator.
    return s;                 // Return sum.
}

int main (void) {
    printf ("%i", sum(1, 2, 3));   // Test it with 1, 2 and 3 (should print 6).
}
paxdiablo
that's the best description I've ever seen of a variable-length argument list
warren
Yeah it is. Pax you should write textbooks!
Peter Wone
A: 

In this code the sum macro converts the sum(1,2,3) call in main into a call to _sum by using sizeof to calculate the number of elements sum is called with. The size of an int array with three values is going to be 3 * sizeof(int), so dividing by sizeof(int) yields three again.

Michiel Buddingh'
A: 

Given an integar array, it will sum up all of its elements and return that value.

David Andres
A: 

The preprocessor here uses variable number of arguments variadic macro. Rest it simply creates an array from argument list and manipulates it

Neeraj
+4  A: 

Let's look at the expansion of the sample invocation sum(1, 2, 3) of the macro

#define sum(...) \
    _sum(sizeof((int []){ __VA_ARGS__ }) / sizeof(int), (int []){ __VA_ARGS__ })

The ... mean it's a variadic macro, ie it takes any number of comma-seperated arguments. On expansion, the special preprocessor token __VA_ARGS__ will be replaced with these arguments, ie

(int []){ __VA_ARGS__ }

expands to

(int []){ 1, 2, 3 }

This is a compound literal: C99 allows to create objects with automatic storage duration on-the-fly via such a typed initialization list.

It's important that the size of the array will be inferred: It won't have incomplete type int [] but will be of type int [3], ie

sizeof((int []){ 1, 2, 3 }) = sizeof(int [3]) = 3 * sizeof(int)

To get the number of elements, divide by sizeof(int).

The macro invocation sum(1, 2, 3) is therefore equivalent to the C90 code

int tmp[3] = { 1, 2, 3 };
_sum(3, tmp);
Christoph