Shock horror! I learned something about arrays in C

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:

image

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:

image

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:

image

In fact, all these declarations for process_array() are semantically identical; the code generated is the same in each case:

image

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:

image

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:

image

However, within our function we may not get the answer we expect:

image

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:

image

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:

image

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:

image

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):

image

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 ( * )[10]). 

The array-pointer is strongly typed, so the following code will fail to compile:

image

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):

image

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).

Glennan Carnie
Dislike (1)
Website | + posts

Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.

He specialises in C++, UML, software modelling, Systems Engineering and process development.

About Glennan Carnie

Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry. He specialises in C++, UML, software modelling, Systems Engineering and process development.
This entry was posted in C/C++ Programming and tagged , , , , . Bookmark the permalink.

16 Responses to Shock horror! I learned something about arrays in C

  1. Michal Mocny says:

    Yes, this is an interesting lesson to learn, and you've done a great job of demonstrating the situation.

    For another fun tidbit, try playing with extern declarations and array/pointer degrading.

    foo.cc:
    Foo foo[5] = ...;
    vs
    Foo *foo = ...;

    bar.cc:
    extern Foo foo[5];
    vs
    extern Foo *foo;
    int main() { /* use foo */ }

    Things do compile, and link, and sometimes even produce (surprising) results..

    Like (0)
    Dislike (2)
  2. glennan says:

    Oooh, yes indeedy!

    Anyone got any more abuses of C's arrays? Or, even better, ways to avoid abuses!

    Like (0)
    Dislike (1)
  3. DaveP says:

    Thanks for showing something so "obvious" and basic can mess you up, keeps us on our toes.

    One I hit early on in my C-ing that I didn't believe is related:

    Declare a string in one file :

    = = = main.c = = =
    main() {
    char mystr[] = "Howdy";
    }

    and try and ref it as an extern pointer in another :

    = = = funcs.c = = =
    extern char* mystr;
    myfunc() {printf("Say %s\n",mystr);}

    Wait "name of string is pointer to that array" - right ? No - first is type 'char array' and second is type 'char pointer' so (hopefully) compile error.

    Could declare second as 'extern char mystr[]' or first as 'char *mystr = "Howdy"; '

    Again, basic but made me scratch my head fist time I hit lo some 20 years ago.

    Like (1)
    Dislike (1)
  4. DaveP says:

    Sorry MichalM had same example - got too busy typing and didn't see it.

    Like (0)
    Dislike (1)
  5. Michal Mocny says:

    @DaveP

    Actually the case of string literals is a particularly nasty case, so great of you to draw attention to it.

    Like (0)
    Dislike (1)
  6. Satheesh G says:

    It's really interesting lesson.

    thanks

    Like (1)
    Dislike (1)
  7. Bare arrays are evil! Wrap them in structures, then you don't have to bother with this awful syntax when passing them to and using them in functions.

    With respect to C++, I'm surprised that MISRA-C++ 2008 favours this arcane syntax rather than recommending some sort of array class, even a trivial homebrewed one. Perhaps std::vector is not such a good candidate for this but maybe MISRA's stance will change, eventually, now that we have std::array in C++11.

    Like (0)
    Dislike (2)
  8. cerkiewny says:

    The thing that you probably didn't know about is
    that this semantics are identical as well:

    a[5]
    5[a]

    Like (1)
    Dislike (2)
  9. glennan says:

    I like this one. It messes with people's heads 🙂

    Like (1)
    Dislike (2)
  10. Shippers says:

    This is a nice trick! I like to expand on this by throwing a couple on consts into the definition of process_array:
    void process_array( const uint32_t (*const pArray)[10])

    The first const stops you from doing this (which is useful if your function shouldn't be modifying the contents of the array):
    (*pArray)[i] = 123;
    Depending on what you want the function to do, you might want to omit this const.

    More importantly, the second const stops you from doing this:
    pArray = NULL;
    In most cases, you probably don't want pArray pointing somewhere else, so this is useful protection!

    Like (1)
    Dislike (2)
  11. Pingback: Links da semana #18 | Blog do Sergio Prado

  12. Dave says:

    Interesting and well written article - here is the same thing for c++ using references:

    void process_array(uint32_t (&rArray)[10])
    {
    for (int i = 0; i < sizeof(rArray); ++i)
    {
    printf("%d ", rArray[i]);
    }
    }

    Like (0)
    Dislike (2)
  13. igorjirkov says:

    nice article! I would rather add 'static' to the array dimension like this:

    int param[static 10]

    https://hamberg.no/erlend/posts/2013-02-18-static-array-indices.html

    Like (0)
    Dislike (1)
  14. Good point - provided your compiler actually will emit a diagnostic for this! 🙂

    Like (1)
    Dislike (1)
  15. Julian says:

    Good article! Thanks!
    Do you have any idea why it didn't make it into the MISRA-C rules?

    I have to handle a lot of arrays in my source code and I'd love have as many potential programming errors as possible being caught by the compiler (or at least by static code analysis).

    Like (0)
    Dislike (0)
  16. No, sorry - I guess it made be difficult to word a specific rule around this syntax? But I can see it being a goof 'guideline'.

    Thanks,

    Like (0)
    Dislike (0)

Leave a Reply