Every so often you pick up a snippet of information that completely changes the way you view things. This week, it’s the use of arrays as function parameters.
At first glance the code horrified me (as I’m sure it will horrify some of you out there!) but as I’ve played with it I can see real merit in the technique.
Arrays, pointers and syntactic sugar
In C there is a close (if somewhat messy!) relationship between arrays and pointers. As far as the C compiler is concerned an array is merely a contiguous sequence of objects (all of the same type). Pointer arithmetic semantics ensure that elements can be accessed as offsets from the array’s base address. The array (‘’) notation is syntactic sugar to hide these details from the programmer:
Arrays as function parameters
When passing arrays to functions (as parameters) things can get a little confusing. It is not possible to pass an array by-value to a function, so the function process_array() below does not make a copy of the array:
The array parameter degrades to a pointer – the address of the first element; so we could (and many C programmers do) just as legitimately write the following and get the same result:
In fact, all these declarations for process_array() are semantically identical; the code generated is the same in each case:
A word of warning here: as we discussed above the array name yields a constant value that is the address of the first element. In the case of a function parameter, though, we could easily delude ourselves:
What looks like an array name is (of course) just a (non-const) pointer; which can be modified, either deliberately or accidently.
Once inside our function, very commonly we wish to know the number of elements in the array. The sizeof() operator yields the amount of memory an object occupies; so for an array this is the number of elements times the size of the element. A simple piece of macro programming can yield the number of elements:
However, within our function we may not get the answer we expect:
In a 32-bit architecture we’ll always get an answer of 1, irrespective of the actual number of elements in the array!
If we re-write the function to the (semantically-identical, remember!) equivalent and expand the macro:
We’re dividing the size of a pointer by the size of an int. Oops.
Because of this it is normal practice to pass an additional parameter, the number of elements in the (supplied) array:
Is there any other way?
An alternative (I’m loathe to use the word ‘better’ here) approach is to use the mechanism preferred for all large data types – pass-by-pointer.
The syntax for passing an array by pointer is a little unwieldy due to C’s precedence rules:
The function signature declares that process_array() is expecting a pointer to an array of (exactly!) 10 uint32_t objects.
To call the function, you must pass the address of the array (just as you would with a struct):
This may cause confusion for some readers – They’re thinking “Hang on! The array name yields the address of the array! Why isn’t he just calling the function with the name of the array?”. Remember: the array name yields the address of the first element (and will be, in our case, of type uint32_t*). We need a pointer to an array (of 10 uint32_t) so we must use the address-of operator (which will yield a pointer of type uint32_t ( * )).
The array-pointer is strongly typed, so the following code will fail to compile:
In case you were wondering, the following code will now work as expected (although, since you are specifying the expected size of the array it is a little redundant):
Although unusual for arrays (that is, not used often) this approach has a number of benefits:
- The function declaration explicitly states the size of the array it is expecting.
- It allows compile-time type checking
- It is consistent with passing structs
- The ARRAY_SIZE macro can be used on the function parameter (correctly)
The above code is actually the preferred mechanism for passing arrays in MISRA-C++ 2008 (Rule 5-2-12) but is not included in the MISRA-C 2012 rules (for some reason).
Latest posts by Glennan Carnie (see all)
- Technical debt - August 22, 2018
- Your handy cut-out-and-keep guide to std::forward and std::move - April 26, 2018
- Setting up Sublime Text to build your project - April 12, 2018