Prev | Contents | Next

33 Arrays Part II

We’re going to go over a few extra misc things this chapter concerning arrays.

They’re not super-commonly seen, but we’ll peek at them since they’re part of the newer spec.

33.1 Type Qualifiers for Arrays in Parameter Lists

If you recall from earlier, these two things are equivalent in function parameter lists:

int func(int *p) {...}
int func(int p[]) {...}

And you might also recall that you can add type qualifiers to a pointer variable like so:

int *const p;
int *volatile p;
int *const volatile p;
// etc.

But how can we do that when we’re using array notation in your parameter list?

Turns out it goes in the brackets. And you can put the optional count after. The two following lines are equivalent:

int func(int *const volatile p) {...}
int func(int p[const volatile]) {...}
int func(int p[const volatile 10]) {...}

If you have a multidimensional array, you need to put the type qualifiers in the first set of brackets.

33.2 static for Arrays in Parameter Lists

Similarly, you can use the keyword static in the array in a parameter list.

This is something I’ve never seen in the wild. It is always followed by a dimension:

int func(int p[static 4]) {...}

What this means, in the above example, is the compiler is going to assume that any array you pass to the function will be at least 4 elements.

Anything else is undefined behavior.

int func(int p[static 4]) {...}

int main(void)
{
    int a[] = {11, 22, 33, 44};
    int b[] = {11, 22, 33, 44, 55};
    int c[] = {11, 22};

    func(a);  // OK! a is 4 elements, the minimum
    func(b);  // OK! b is at least 4 elements
    func(c);  // Undefined behavior! c is under 4 elements!
}

This basically sets the minimum size array you can have.

Important note: there is nothing in the compiler that prohibits you from passing in a smaller array. The compiler probably won’t warn you, and it won’t detect it at runtime.

By putting static in there, you’re saying, “I double secret PROMISE that I will never pass in a smaller array than this.” And the compiler says, “Yeah, fine,” and trusts you to not do it.

And then the compiler can make certain code optimizations, safe in the knowledge that you, the programmer, will always do the right thing.

33.3 Equivalent Initializers

C is a little bit, shall we say, flexible when it comes to array initializers.

We’ve already seen some of this, where any missing values are replaced with zero.

For example, we can initialize a 5 element array to 1,2,0,0,0 with this:

int a[5] = {1, 2};

Or set an array entirely to zero with:

int a[5] = {0};

But things get interesting when initializing multidimensional arrays.

Let’s make an array of 3 rows and 2 columns:

int a[3][2];

Let’s write some code to initialize it and print the result:

#include <stdio.h>

int main(void)
{
    int a[3][2] = {
        {1, 2},
        {3, 4},
        {5, 6}
    };

    for (int row = 0; row < 3; row++) {
        for (int col = 0; col < 2; col++)
            printf("%d ", a[row][col]);
        printf("\n");
    }
}

And when we run it, we get the expected:

1 2
3 4
5 6

Let’s leave off some of the initializer elements and see they get set to zero:

    int a[3][2] = {
        {1, 2},
        {3},    // Left off the 4!
        {5, 6}
    };

which produces:

1 2
3 0
5 6

Now let’s leave off the entire last middle element:

    int a[3][2] = {
        {1, 2},
        // {3, 4},   // Just cut this whole thing out
        {5, 6}
    };

And now we get this, which might not be what you expect:

1 2
5 6
0 0

But if you stop to think about it, we only provided enough initializers for two rows, so they got used for the first two rows. And the remaining elements were initialized to zero.

So far so good. Generally, if we leave off parts of the initializer, the compiler sets the corresponding elements to 0.

But let’s get crazy.

    int a[3][2] = { 1, 2, 3, 4, 5, 6 };

What—? That’s a 2D array, but it only has a 1D initializer!

Turns out that’s legal (though GCC will warn about it with the proper warnings turned on).

Basically, what it does is starts filling in elements in row 0, then row 1, then row 2 from left to right.

So when we print, it prints in order:

1 2
3 4
5 6

If we leave some off:

    int a[3][2] = { 1, 2, 3 };

they fill with 0:

1 2
3 0
0 0

So if you want to fill the whole array with 0, then go ahead and:

    int a[3][2] = {0};

But my recommendation is if you have a 2D array, use a 2D initializer. It just makes the code more readable. (Except for initializing the whole array with 0, in which case it’s idiomatic to use {0} no matter the dimension of the array.)


Prev | Contents | Next