top | item 5239143

A nice, little known C feature: Static array indices in parameter declarations

252 points| ehamberg | 13 years ago |hamberg.no | reply

120 comments

order
[+] tptacek|13 years ago|reply
I am convinced there is some kind of long-term drinking game going on in the C standards committee to see how many different uses they can come up with for the word "static".
[+] matthavener|13 years ago|reply
They don't really have a choice, do they? If you want to add features you can either: 1) make a new reserve word, possibly breaking existing code 2) reuse a reserve word in a new context.
[+] bunderbunder|13 years ago|reply
With bonus points for uses that have absolutely nothing to do with the word's common meaning.
[+] unimpressive|13 years ago|reply
"Nobody really knows what the Bourne shell's grammar is. Even examination of the source code is little help."

— Tom Duff

Not exactly the same situation, considering that compilers work.

[+] Evbn|13 years ago|reply
Hooray for forward compatibility. "static" means "Semantic Token Always Taken In Context."
[+] matthavener|13 years ago|reply
In C++, you can do this by passing the array by reference:

    void foo(int (&foo)[10]) {}
However, that is not "minimum of 10" but "exactly 10".

You can also get the size at compile time:

    template <size_t N> void foo(int (&foo)[N]) { int newarry[N+2]; }
I've used the above for something like this:

    template <size_t N> void safe_strcpy(char (&buf)[N], const char *src) { 
      strncpy(buf, src, N-1); 
      buf[N-1] = 0; 
    }
[+] pyrtsa|13 years ago|reply
And, by extending your second example, we can enforce the minimum array size with std::enable_if (C++11; boost::enable_if_c with C++03):

    template <size_t N>
    typename enable_if<(N >= 10), void>::type
    foo(int (&bar)[N]) { ... }
EDIT: Changed condition from N >= 0 into a more meaningful one. X-)
[+] chc|13 years ago|reply
This works with pointers in both C and C++. int (*foo)[10] is a pointer to an array of exactly 10 elements. The novel thing being pointed out in the OP is the "10 or more" aspect.
[+] Nav_Panel|13 years ago|reply
Doesn't seem to work for me using GCC 4.7.2.

flags: -g -Wall -Wextra -std=c99 -pedantic

The following code compiles and runs (for me at least) with no errors. Tried with both stack and heap allocated arrays of various sizes.

    #include <stdlib.h>
    
    void foo(int array[static 10]) {
      (void) array; // suppress unused var compiler warning
      return;
    }
    
    int main () {
      int *x = calloc(10, sizeof(int));
      int *y = calloc(9, sizeof(int));
      int *z = calloc(11, sizeof(int));
      foo(x);
      foo(y);
      foo(z);
      foo(NULL);
      int a[9];
      int b[4];
      int c[11];
      foo(a);
      foo(b);
      foo(c);
      return 0;
    }
[+] ambrop7|13 years ago|reply
I've tried this, and gcc indeed doesn't produce warnings even with -Wall -Wextra. Clang does however without needing any flags.
[+] jrajav|13 years ago|reply
I can confirm. Perhaps this is a bug? As per the C99 standard [Clause 6.7.5.3: Function Declarators, point 7]:

A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation. If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.

Since that's a 'shall' declaration, shouldn't it at least throw out a warning?

[+] onedognight|13 years ago|reply
> The information provided by static in parameter array declarators is not used for optimization.

This is not a reason to avoid the construct, but as yet gcc[1] doesn't optimize it either.

[1] http://gcc.gnu.org/c99status.html

[+] niggler|13 years ago|reply
Not in front of a computer, but have you tried -std=gnu99
[+] adamnemecek|13 years ago|reply
Can someone recommend a resource that talks about some interesting C stuff similar to this? I've looked at "Expert C Programming: Deep C Secrets" but found it a bit outdated. And the C standard is a bit dry :-).
[+] sramsay|13 years ago|reply
You might look at 21st Century C: C Tips from the New School by Ben Klemens. It's very new (November 2012), and has some really nice stuff in it.

It's getting mixed reviews, but I really found it useful (even if I, like others, disagree with some of his tips). It's particularly good at sorting out what you can do in C99 and C11.

[edit: He has a really useful section on sorting out the different meanings of "static" in C, though I don't recall this being one of them.]

[+] silentbicycle|13 years ago|reply
The International Obfuscated C Code Contest (http://ioccc.org/) has some real gems.

Compiling another language to C is good way to learn a lot about C's nooks and crannies.

You could also look at C coding standards such as the MISRA guidelines. While many things they complain about should be obvious, there will inevitably be some really obscure things they urge you not to try.

[+] matthiasv|13 years ago|reply
Unfortunately, gcc does not warn. I can pass a NULL pointer and arrays that are too small. But on the other hand, there is very little use, because arrays usually contain an arbitrary number of elements. This might be useful for matrices and vectors, but then I would rather wrap them in a struct anyway.
[+] nathell|13 years ago|reply
I thought I knew C.
[+] shurcooL|13 years ago|reply
This is one of the things that attracts me to Go (and developing tools for working with it, which requires parsing the language, etc.). It's much easier to keep the entire language spec in your working memory, because it all fits in http://golang.org/ref/spec.
[+] niggler|13 years ago|reply
What makes me wary of using these uncommon constructs is that someone editing code later may make a change based on a superficial understanding (and break the build)
[+] vvhn|13 years ago|reply
great, just what we need - yet another overload of the static keyword :-).
[+] PySlice|13 years ago|reply
Why do many features added to C after the first standard have to be quirky and slightly incompatible (with C++, with existing implementations, etc.) like this?

Other examples are inline (different from C++, makes use of weird combinations with static and extern) and tgmath (compiler magic inaccessible to user-defined functions until C11).

They also seem to __barely__ improve the language without ever being "cool" or "interesting".

At least C++ has some standard data structures...

PS: Even Python has binary literals, while they were deemed "not useful enough" for C.

[+] jmaygarden|13 years ago|reply
I've never seen this before:

  void bar(int myArray[static 10])
Is that standard, and if so, how long has it been?

I also ran across this construct in a quick search [1]:

  void bar(int myArray[const])
[1] http://stackoverflow.com/questions/3693429/c-parameter-array...
[+] ehamberg|13 years ago|reply
From http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf:

6.7.5.3 point 7: “If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.”

[+] cedricd|13 years ago|reply
Interesting, but the one comment I haven't seen yet on this thread is 'Why is this useful enough to be a compiler feature?'. I think this is especially relevant with a relatively slim language like C.

In the rare cases you need to do this sort of check why not just write a simple sizeof test?

[+] mauvehaus|13 years ago|reply
The compiler doesn't pass any size information when you pass an array into a function. The function just gets a pointer. If you take the sizeof the array, you'll get the size of a pointer on your system:

[eric@rangely foo]$ cat foo.c

    #include <stdio.h>

    int test (int arr[10])
    {
        printf ("%lu\n", sizeof (arr));
        return 0;
    }

    int main (int argc, char *argv[])
    {
        int arr[5];
        test (arr);
        return 0;
    }
[eric@rangely foo]$ gcc foo.c -Wall -o foo && ./foo

8

(EDIT: fixed formatting)

[+] kelnos|13 years ago|reply
As another poster said, the compiler can't know inside the function body the size of the array passed if you just use sizeof.

More to a style/safety point, if I ever see a function that expects an array and doesn't also take the size of that array as another parameter, that's a bug waiting to happen.

Especially in this case since this feature seems to only throw a warning on recent versions of clang, and more or less nothing else.

[+] corysama|13 years ago|reply
Wouldn't you need to put your sizeof test outside of every call to your function? Inside the function it would only know the declared parameter's type, so it would have no idea what sized array you actually passed.
[+] apaprocki|13 years ago|reply
Using 'static' in this way compiles with both Oracle Studio 12 and IBM xlc 11, but neither exhibit the behavior that gcc is shown to have in the article. Passing in both NULL as well as an array of shorter length work just fine with no warning/error from the compiler.

So, YMMV.

[+] yxhuvud|13 years ago|reply
[I'm away from a C compiler and can't test]:

What happen if you do

void bar(int fooArray[static 0]) {} ?

Is NULL allowed?

[+] ehamberg|13 years ago|reply

    test.c:3:30: warning: 'static' has no effect on zero-length arrays [-Warray-bounds]
[+] quasive|13 years ago|reply
According to section 6.7.5.2p1 of C99: “If the expression is a constant expression, it shall have a value greater than zero.”

The “expression” here refers to an expression in between [] in an array declaration; so the declaration of size 0 is a constraint violation and requires a diagnostic. You can get gcc and clang to issue a relevant diagnostic with “-std=c99 -pedantic”.

[+] unknown|13 years ago|reply

[deleted]

[+] derleth|13 years ago|reply
A whiff of C++ would be fine, as long as they don't take enough to make people think they have to pick a subset of C. One of C's strengths compared to C++ is that C is one language with a small number of dark corners, not multiple languages trying to share a single standards document.
[+] huhsamovar|13 years ago|reply
This is legitimately awesome, and allows for cleaner code. Nice post!