Adding Values from Two Arrays

In last week’s Lesson, I covered a function present in other programming languages but absent in C: concatenating arrays. This time, the topic is similar: adding two arrays. Yes, such functions exist in other languages, but in C you must write one yourself.

I’m unsure when it would be necessary to add values from two arrays — or to perform any other math on these values. I’ve never had the need, though I find such exercises challenging.

The function’s goal is to swallow two arrays, adding sequential element values from each, then setting the results in a third array, which the function returns.

For example, you start with these two arrays:

int a[] = { 11, 22, 33, 44, 55 };
int b[] = { 89, 78, 67, 56, 45 };

The function, call it intarrayadd(), swallows these two arrays — and their element count — then returns a new array with the totals:

100 100 100 100 100

As I ruminated on this topic, one thought immediately came to me: What if the arrays are of different sizes? Obviously, I didn’t want to truncate the result and lose data. So I figured I would do what happens in real life: If extra items are left over, they’re included in the result as-is. This rule implies that the array generated must be equal in its element count to the larger of the two arrays passed.

Here is my array adding function, which works specifically with integer arrays:

/* concatenate two integer arrays */
int *intarrayadd(int *a, int asize, int *b, int bsize)
{
    int *c,larger,smaller,x;

    /* allocate for the larger array */
    larger = asize > bsize ? asize : bsize;
    /* and get the smaller size as well */
    smaller = larger > asize ? asize : bsize;

    /* allocate storage for new array */
    c = malloc( sizeof(int) * (larger) );

    /* add if the allocate in successfull */
    if( c!=NULL )
    {
        /* copy up to the size of the
           smaller array */
        for( x=0; x<smaller; x++ )
            *(c+x) = *(a+x) + *(b+x);

        /* just copy over the remaining values
           from the larger array */
        if( asize==larger )
        {
            /* use existing value of x */
            /* array 'a' is larger */
            for( ; x<larger; x++ )
                *(c+x) = *(a+x);
        }
        else
        {
            /* array 'b' is larger */
            for( ; x<larger; x++ )
                *(c+x) = *(b+x);
        }
    }

    return(c);
}

As with array concatenation, the intarrayadd() function is specific to the integer data type. Both arrays are passed, along with their sizes.

Immediately, tests made to determine which array is larger and and which is smaller, assuming they’re of different sizes:

/* allocate for the larger array */
larger = asize > bsize ? asize : bsize;
/* and get the smaller size as well */
smaller = larger > asize ? asize : bsize;

Storage is then allocated based on the size of the larger array:

c = malloc( sizeof(int) * (larger) );

Upon success, the values of the two arrays are added, stored in newly created array (buffer, actually) c:

for( x=0; x<smaller; x++ )
    *(c+x) = *(a+x) + *(b+x);

Depending on which array is larger, a second set of for loops copy over the remaining elements from the larger array into new array (buffer) c. If the arrays are identical in size, the value of variable larger is equal to variable x, so nothing is copied.

The newly-created array is returned or, should memory allocation fail, NULL is returned.

The main() function outputs both original arrays, then the newly-created array, returned from the intarrayadd() function. The larger array size must be calculated again in the main() function to ensure that all elements in the new array are output. Here is a test run:

Array 'a': 11 22 33 44 55
Array 'b': 89 78 67 56 45 1 2 3
Result: 100 100 100 100 100 1 2 3

Click here to view the full code on GitHub. Again, I don’t know when I would ever use such a function. My point is that although other programming languages have such functions in their libraries, in C you must code your own. I find such challenges engaging and fun.

3 thoughts on “Adding Values from Two Arrays

  1. Now, I know that this is a blog about “C”… however, as I too find such challenges engaging and fun, I began to wonder, if the SIMD paradigm could yield an even faster solution (if the problem specification was simplified a bit), i.e. would it be worthwhile to consider a SSE2 solution, if exactly 4 int’s were to be added at a time?

    /*N.b. [static 4] promises the compiler, that (at least) 4 elements will be passed */
    int *int4arrayadd (int c [static 4], int const a [static 4], int const b [static 4]) {
    for (size_t x = 0; x < 4; x++)
    *(c + x) = *(a + x) + *(b + x);
    return (c);
    }

    int *sse2arrayadd (int c [static 4], int const a [static 4], int const b [static 4]) {
    __m128i augend = _mm_loadu_si128(a); /* movdqu xmm0, [arr_a] */
    __m128i addend = _mm_loadu_si128(b); /* movdqu xmm1, [arr_b] */
    __m128i sum = _mm_add_epi32(augend, addend); /* paddd xmm1,xmm0 */
    _mm_storeu_si128(c, sum); /* mov rax, [arr_c] */
    return (c); /* movdqu [rax], xmm1 */
    }

    … that is to say: is it faster to load the 4 integers in ‘a[]’ and ‘b[]’ into (two) 128-bit registers xmm0 and xmm1, add the 4 numbers in one go (and write the resulting 128 bits to an array ‘c[]’), or may this even be slower than a simple (possibly unrolled) ‘for’ loop?

    On current machines the answer seems to be: sse2arrayadd() is about twice as fast if -O2 is used; if -O3 is used, however, this advantage shrinks to about 6%.

    I posted detailed timings on GitHub (should anyone be interested):
    https://tinyurl.com/4ma49vsd

  2. There’s a massively popular Python library called NumPy which basically provides a typed array and a set of methods for manipulating them, including addition like in this post. It’s actually written in C and so is very fast and apparently uses SIMD. It’s FOSS so maybe they’d like to have you on board . . . 🙂

    https://github.com/numpy/numpy

  3. Thanks for the tip 🙂

    An intriguing thought for sure… however, while I have (now and then) tipped a toe into assembly language programming over the years, I’ve long since realized that I’ll probably never ascend to being a world-class assembly language programmer (even if standards were lowered a bit and gods like Michael Abrash or Terje Mathisen were left out of the picture).

Leave a Reply