10 ms·
A nice, little known C feature: Static array indices in parameter declarations
- vvhn 14y agogreat, just what we need - yet another overload of the static keyword :-).
- matthavener 14y agoIn 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 14y agoAnd, 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 14y agoThe 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!"); ... }
- pyrtsa 14y agoCorrect. The key difference is that the enable_if version can be overloaded with mutually exclusive requirements: template <size_t N> typename enable_if<(1 < N && N < 10), void>::type foo(int (&bar)[N]) { // called when 1 < N && N < 10 } template <size_t N> typename enable_if<(N >= 10), void>::type foo(int (&bar)[N]) { // called when N >= 10 } OTOH, the key advantage in static_assert is the meaningful error message.
- abcd_f 14y agoThis will generate separate function bodies for different values of N.
- zokier 14y agoI 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 14y agoOn 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).
- huhtenberg 14y ago... and just as I was going to make a smart-ass joke about Boost probably having another contrived contraption just for that - there it is, already sneaked into the standard too.
- chc 14y agoThis 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.
- cliffbean 14y agoThat's true, until you forget and type foo[i] instead of (* foo)[i]. Of course, compilers will usually catch that mistake at compile time... There'a an interesting analogy with structs here. K&R invented the -> operator specifically to obviate (* ptr).member. It's too bad that arrays took a different route through C's history and didn't manage to end up in a place where a similar convenience would make sense.
- jmaygarden 14y agoI'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-declarators http://stackoverflow.com/questions/3693429/c-parameter-array...
- ehamberg 14y agoFrom http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf 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 14y agoSo, it was added in C11? UPDATE: Thanks. The 2005 date on the linked document threw me off.
- ehamberg 14y agoNo, C99 – this is the the C99 standard with corrigenda TC1, TC2, and TC3 included.
- nathell 14y agoI thought I knew C.
- niggler 14y agoWhat 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)
- ambrop7 14y agoMuch less likely than someone editing code later using his superficial understanding of C++ :)
- zxcdw 14y agoOr Ruby. Or C. Or Scala. Or Haskell. Or JavaScript. Or Go. You get the point I hope.
- klibertp 14y agoIt's not that easy, I'm afraid. Languages do differ in many areas and one of them is how easy it is to make a mistake in one. I think Go was designed with this in mind? Also, Haskell's type system guards against this. On the other hand C does nothing to prevent you from shooting yourself in the foot and C++, while improving some things, makes it overall worse because of sheer amount of constructs in the language. Ruby and JS are better in that they run on VMs and so won't segfault (that often), but other than that they do very little to help avoid making mistakes (implicit undefineds passed to functions in JS...). Anyway, languages are not created equal and one thing a language designer can optimize for is to reduce the probability of programmer making mistake. That's only one of the variables however and sometimes it's the other goals that are more important and then we get languages like C++. That's not to say it's bad, it's just optimized for different things.
- niggler 14y agoAfter the fifth time dealing with subordinates misunderstanding template constructs I decided to throw out all of the C++ code and reimplement in "simple" C and x64 assembly -- at least now people don't mess with the assembly
- matthiasv 14y agoUnfortunately, 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.
- yxhuvud 14y ago[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 14y agotest.c:3:30: warning: 'static' has no effect on zero-length arrays [-Warray-bounds]
- deleted 14y ago[deleted]
- quasive 14y agoAccording 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”.
- tptacek 14y agoI 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".
- bunderbunder 14y agoWith bonus points for uses that have absolutely nothing to do with the word's common meaning.
- Evbn 14y agoHooray for forward compatibility. "static" means "Semantic Token Always Taken In Context."
- crest 14y agoToday i learned what s.t.a.t.i.c. stands for.
- matthavener 14y agoThey 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 14y agoOne better choice that comes to mind: void bar(int myArray[10+]);
- vexal 14y agoThat's not better. It's more confusing to see a symbol that's normally an operator than it is to see a keyword.
- deleted 14y ago[deleted]
- Nav_Panel 14y agoDoesn'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 14y agoI've tried this, and gcc indeed doesn't produce warnings even with -Wall -Wextra. Clang does however without needing any flags.
- qznc 14y agoMy clang (3.0-6ubuntu3) does not.
- niggler 14y agoNot in front of a computer, but have you tried -std=gnu99
- DiabloD3 14y agoThats 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 14y agoIt'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.
- deleted 14y ago[deleted]
- derleth 14y agoA 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.
- pjmlp 14y agoAnd yet using modern C++ it is possible to have safer code than C, if everyone on the team plays by the rules that is. Personally I would rather use another language in the Pascal family (Turbo Pascal refugee), but it is not always possible to choose.
- derleth 14y agoAnother advantage of C over Old C++ is that there's one C, defined by one standards document and maybe a few common features that are not standard but reliably present on most compilers and hardware. There isn't a list of features you're theoretically able to use but the compilers don't support (templates in Old C++) or common things you do differently in every implementation. Pascal figures in this in that the official standard Pascal was basically unworkable as a language, due to features like the size of an array being an obligatory part of its type and the resulting lack of a way to write a function that could handle more than one size of array. Having it be optional, as we see here in C, is really the only way to go unless an array knows its own size and won't let you overstep the bounds.
- pjmlp 14y ago> Another advantage of C over Old C++ is that there's one C, defined by one standards document and maybe a few common features that are not standard but reliably present on most compilers and hardware. There isn't a list of features you're theoretically able to use but the compilers don't support (templates in Old C++) or common things you do differently in every implementation. If only this was true http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html http://blog.llvm.org/2011/05/what-every-c-programmer-should-... I had to develop with multiple C commercial compilers between 1999 and 2002, across several OS. The code had quite a few #ifdefs because of them. Are you aware that C11 has optional features? > Pascal figures in this in that the official standard Pascal was basically unworkable as a language, due to features like the size of an array being an obligatory part of its type and the resulting lack of a way to write a function that could handle more than one size of array. Having it be optional, as we see here in C, is really the only way to go unless an array knows its own size and won't let you overstep the bounds. The first ISO standard yes, but all Pascal dialects always had feature parity with C, while being more type safe, fast compile times and direct support for modules. Most of it was made part of the ISO Extended Pascal standard, which most people ignored as the industry cared more about Turbo Pascal compatibility. Both solve your arrays example. The common complaint that the stronger type checking languages impose performance penalty with arrays bound checking is always wrong, as the compilers allow for it to be turned off. In both cases it is the problem with standards and vendor differentiation, you seldom get a 100% compliant implementation of any standard.
- adamnemecek 14y agoCan 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 14y agoYou 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 14y agoThe International Obfuscated C Code Contest (http://ioccc.org/ 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.
- apaprocki 14y agoUsing '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.
- cedricd 14y agoInteresting, 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 14y agoThe 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)
- corysama 14y agoWouldn'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.
- kelnos 14y agoAs 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.
- PySlice 14y agoWhy 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.
- huhsamovar 14y agoThis is legitimately awesome, and allows for cleaner code. Nice post!
- galaktor 14y agoa 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 http://golang.org/doc/effective_go.html#arrays
- FlawedDesign 14y agoLive test case: http://liveworkspace.org/code/2uk7hc$18 http://liveworkspace.org/code/2uk7hc$18