The peril of index(3)

2009-11-05 21:34:06 UTC

This is mainly for Andy Finnell on Twitter, who wonders why some of us avoid naming variables index.

I pointed out that there is a function in standard C named index, and this causes one of two problems: If you declare a variable named index, you have shadowed the function and should get a warning for that; if you fail to declare the variable, you pass the pointer to the index function as your array index, which is probably not what you intended.

I say “should” there because, as he noted in his response, the shadowed-name warning is off by default. You should turn it on, because it catches bugs. In fact, the index bug is one that it can prevent.

Suppose you do name a variable index, and either you don’t have the shadowed-name warning turned on or you ignore it. You initialize the variable with an index, but don’t otherwise assign to it. Then, you attempt to access an object in an array by this index.

All well and good so far. index is a variable, so everything works as intended.

But then, one of several things happens:

  1. You comment out both the declaration and the usage of index, for whatever reason, but then you uncomment the usage but forget to uncomment the declaration.
  2. You update and/or merge in your version-control system, or otherwise apply one or more diffs. Usually, this works, but today isn’t your lucky day: The merge breaks your source code. Perhaps it introduces conflicts, and you resolve them incorrectly. Or maybe it breaks the code silently (e.g., by merging in another branch’s division of this function into two).
  3. You move the code to another location, but you forget to move half of it, or you move one half and delete another, forgetting that the declaration of index was in the code you deleted.

You had a variable named index, but now you don’t—but the index function is always there*. Since there is something named index, your code compiles. It’s the wrong type, so you’ll get a warning, but maybe you don’t notice it.

Then you run the code and it crashes. Why? Because you passed a function as the index into an array.

In the worst possible case, it was #2 and you weren’t aware that this code was affected. Maybe you’d been working on something else. Anyway, since you hadn’t been working on the now-broken code, you aren’t testing it**, so you don’t know that it’s now broken.

So you ship it. You ship this index-way-out-of-range crasher. And then your user runs the code and gets the crash.

This isn’t theoretical. I’ve had this happen more than once (fortunately, not in the hands of a user). It’s one reason why I turn on the shadowed-name warning and “Treat Warnings as Errors”, and it’s the reason why I never use index as a variable name.

UPDATE 2009-12-05: To clarify, this problem does not affect C arrays, as C does not allow you to use a pointer in an array subscript. It mainly affects higher-level array interfaces, such as Cocoa’s NSArray.

* Assuming that, directly or indirectly, you’ve included string.h. If you’re using Cocoa, you have (Core Foundation includes it).

** Unless, of course, you have automated test cases covering this code, and you run those.

4 Responses to “The peril of index(3)”

  1. Andy Finnell Says:

    I see what your saying, but if you remove your declaration of index (accidental or otherwise) you get all sorts of warnings about trying to use a pointer as an integer (at least I did when I tried it). Depending on what you try to do with index (such as math) the compiler will even throw errors. I can appreciate that you’ve been bitten by this before, but this seems really edge case. I prefer to have readable names, and trust the compiler will catch my mistakes.

  2. Peter Hosey Says:

    I haven’t had the compiler throw an error about it once, except when I build with “Treat Warnings as Errors” turned on (which is good for many reasons, including this one). But I have encountered the crash. Less time spent fixing oops-my-variable-declaration-went-missing bugs is more time spent fixing real bugs and adding features.

    And idx really isn’t that unreadable, although I do try to consider whether fooIndex or indexOfFoo might be better without being obnoxiously long. The main thing is to not use index.

  3. Jens Alfke Says:

    I disagree. If you can pass a pointer as an array index and not get a compile error, something’s dangerously wrong with your build process. This is why I ALWAYS, ALWAYS build with -Werror (“Treat warnings as errors”.) There are too many things GCC treats as warnings that are very dangerous and should be fixed immediately. This goes double for Obj-C programming, where things like misspelled selectors only generate warnings.

    I find the shadowing warning useless because it yells about all sorts of names that happen to be used by C and POSIX APIs — not just index but time and a bunch of others. I’d like to have a warning that told me when I was shadowing my own local variables, but I don’t think GCC has that.

  4. Jonathan Mitchell Says:

    GCC 4.2 as shipped with Xcode 3.2.1 does shadow your local variables as well as parameters and globals.

    Warn whenever a local variable shadows another local variable, parameter or global variable or whenever a built-in function is shadowed. [GCC_WARN_SHADOW, -Wshadow]

Leave a Reply

Do not delete the second sentence.