
A nice, little known C feature: Static array indices in parameter declarations - ehamberg
http://hamberg.no/erlend/posts/2013-02-18-static-array-indices.html
======
tptacek
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
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.

~~~
rwmj
Larry Wall (author of Perl) used this as an argument for "sigils", that is,
putting $/@/% in front of every variable.

[http://stackoverflow.com/questions/1091634/why-do-perl-
varia...](http://stackoverflow.com/questions/1091634/why-do-perl-variables-
need-to-start-with-sigils/1092410#1092410)

(I merely state this as fact and offer no judgement!)

~~~
drivers99
Deleted my previous comment because it was wrong (I was saying that it didn't
help with the names of subroutines). It appears you can even have subroutines
named after keywords, but only as long as you put "&" in front of it when you
call it:

    
    
        sub while
        {
            print "It works!\n";
        }
    
        &while();
    

I guess it is still the case that you can break perl code by adding keywords,
if it collides with a subroutine that is called with the "&" symbol. Seems
most code doesn't use "&" though, but it's a relatively quick fix.

~~~
leviathan
Calling a sub with & has a special meaning, it exposes the current argument
list @_ to the called sub. That's why you don't see it in most code.

~~~
drivers99
I did not know that. Interesting. However, it only works if you call the sub
without parentheses like this: &foo

Calling it with parentheses like &foo() would make @_ empty inside of foo. (Or
if you said &foo("whatever") it would pass that as @_ instead.)

    
    
        sub while
        {
                print "@_\n";
                &foo();
        }
    
        sub foo
        {
                print "@_\n";
        }
    
        &while("a","b","c");
    

produces:

    
    
        a b c
        (blank line)
    

as the output, and

    
    
        sub while
        {
                print "@_\n";
                &foo;
        }
    
        sub foo
        {
                print "@_\n";
        }
    
        &while("a","b","c");
    

produces:

    
    
        a b c
        a b c 
    

as the output. At any rate, back to the original topic: it still doesn't
prevent new keywords from potentially colliding. Oh well.

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

~~~
pbsd
The more readable C++11 version would use static_assert:

    
    
        template <size_t N>
        foo(int (&bar)[N]) 
        { 
            static_assert(N >= 10, "Array needs >= 10 elements!"); 
            ...
        }

~~~
zokier
I don't think that is more readable. With enable_if the precondition is
clearly visible at the function header, while the static_assert is inside the
body and thus more easily accidentally ignored.

~~~
obiterdictum
On the other hand, static_assert will have a more helpful compile error for
users of the code. They will clearly see assertion condition that failed,
rather than missing candidate error due to substitution failure (or worse, a
different overload silently considered instead).

------
Nav_Panel
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;
        }

~~~
niggler
Not in front of a computer, but have you tried -std=gnu99

~~~
DiabloD3
Thats a bad way of writing code, to be honest. On projects I run, code has to
be warning free with -Wall -Wextra -pedantic -std=c99

~~~
kelnos
It's not bad at all, at least if you've made the conscious decision to write
GNU C and not std C, and accept that non-gcc compilers (except maybe clang)
may not be able to compile your code.

Unfortunately, though, I believe one of the -std=gnuXX variants is the
default, so most people don't make that a conscious decision.

~~~
niggler
gnu89 is the default. gnu99 will be the default when C99 is fully implemented
(from the manpage, search for /-std=$/)

~~~
simias
C99 is still not fully implemented in GCC? What's missing?

~~~
TorKlingberg
There is a list at <http://gcc.gnu.org/c99status.html>

------
adamnemecek
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
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.]

------
matthiasv
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
I thought I knew C.

~~~
shurcooL
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>.

~~~
stusmall
That's just because its young. The same could have been said for C in the long
long ago. And honestly compared to most of whats out there, C is a tiny
language. Give go time, it'll bloat ;)

~~~
frou_dh
Whether generics get retrofitted will be the measure of this. I suspect they
won't, because by the time a Go 2.0 timeframe arrives, the thinking will be
something like "Time has shown that we thrived without them, so despite
commentator gnashing, they're simply not required."

~~~
shurcooL
It's possible to write tools that generate code instead of building it into
the language too. I wonder if it can be done better than building it into the
language.

~~~
frou_dh
You could contrast achieving generics using C preprocessor gymnastics versus
C++ templates.

~~~
shurcooL
C preprocessor stuff works with text and text only. That's why it's quite hard
to make things elegant. Tools can work with text and anything else you can
think of. So they can potentially be better. But I don't know what could be
done at this point (in fact, this might be a good problem to be analyzed from
a theoretical side).

------
vvhn
great, just what we need - yet another overload of the static keyword :-).

------
PySlice
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
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...](http://stackoverflow.com/questions/3693429/c-parameter-array-
declarators)

~~~
ehamberg
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.”

~~~
jmaygarden
So, it was added in C11?

UPDATE: Thanks. The 2005 date on the linked document threw me off.

~~~
ehamberg
No, C99 – this is the the C99 standard with corrigenda TC1, TC2, and TC3
included.

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

------
apaprocki
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
[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

        test.c:3:30: warning: 'static' has no effect on zero-length arrays [-Warray-bounds]

------
FlawedDesign
Live test case: <http://liveworkspace.org/code/2uk7hc$18>

------
galaktor
a bit off-topic: Golang has a similar feature built into it's type system [1]

"The size of an array is part of its type. The types [10]int and [20]int are
distinct."

[1] <http://golang.org/doc/effective_go.html#arrays>

------
huhsamovar
This is legitimately awesome, and allows for cleaner code. Nice post!

