Old C dog, new C tricks part 2: i won’t use i

See Also: part 1, part 2, part 3, part 4 and part 5.

When I learned BASIC, manuals always showed the FOR/NEXT loop using the variable “I”:

FOR I=1 TO 100:NEXT I

“I” had no idea why this was, back then, but since then I have been told there was some history with some programming language that had certain letters reserved for certain features, such as as I for loops.

But is this true? A quick Google search produced an “AI summary”:

The convention of using “i” as the loop counter variable originates from the mathematical notation where “i,” “j,” and “k” are commonly used as indices or subscripts to represent integers in sequences or arrays. This practice was adopted early in computer science and has persisted due to its conciseness and familiarity among programmers. While any valid variable name could technically be used, “i” serves as a readily recognizable and easily understood placeholder for the loop index, especially in simple iterations. In nested loops, “j” and “k” are conventionally used for the inner loop counters.

– Google AI Summary of my search result.

But that’s not important right now…

I suppose I had a bit of O.C.D. in me, because starting with “I” seemed weird.

I would do my loops starting with “A”.

FOR A=1 TO 100:NEXT A

When variables needed to mean something, I’d use the one or two-character variable name that made sense — NM$ for Name string, UC for User Count, etc. But for loops and other things, I’d use A, B, C, D, etc.

C books were similar, showing “i” for loops:

for (int i=0; i<100; i++)
{
    ...stuff...
}

For some reason, I always used “i” in C rather than a, b, c… My nested loops looked just like the AI summary described:

for (int i=0; i<10; i++)
{
    for (int j=0; j<10; j++)
    {
        for (int k=0; k<10; k++)
        {
            ....stuff....
        }
    }
}

It was at a previous job that another embedded programmer said something to me that changed this forever.

“You can’t search for i…”

– paraphrased quote from embedded programmer I used to work with.

While today there are modern editors that let you specify how a search works — full word or partial, ignore case or case sensitive, and even regular expressions — but you can never depend on having access to those tools. Some jobs are very restrictive about the software you are allowed to install on your work computer. Some simply don’t allow any installs by employees: you get the set of preconfigured apps for the position (Microsoft Office for some roles, compilers and such for others, etc.) and that might be it.

He told me he used “idx” for loops, rather than “I”. And that was enough to change my C coding habits instantly. Even since, when I do a loop, it’s like this…

for (idx=0; idx<100; idx++)
{
    ...stuf...
}

And when I’m looping through things of a given type, it do things like:

for (int cardIdx=0; cardIdx<52; cardIdx++)
{
    ...stuf...
}

Who says you can’t teach an old dog new tricks?

What do you use for your loops? And why?

Comments, if you have them…

27 thoughts on “Old C dog, new C tricks part 2: i won’t use i

  1. William Astle

    It may also be related to Fortran (I think) which defaulted the data type based on the first letter. Hence the joke “God is real unless declared integer”. As I understand it, “I” was the letter that defaulted to integer, maybe due to that mathematical convention.

    Reply
  2. Sean Patrick Conner

    The convention of using i, j, k as index variables comes from FORTRAN where (at least through Fortran 77) you could implicitly define variables based on the first character of the name: any variable starting with I, J, K, L, M or N are integer; all others are real (from the book Problem Solving With FORTRAN 77 that I have on my shelf). This, in turn, comes from mathematical notation where traditionally, those letters were used to indicate successive values (“n sub i” type stuff).

    For my own code, I tend to use i and j for looping through arrays. It’s short, and it’s a common idiom. I absolutely despise Hungarian notation (stuff like cardIdx or lpstrFoo) largely from misuse (the concept is okay, but people tend to use it to indicate type instead of semantic use).

    As an aside, from your posts, I get the impression that most embedded programmers use C like it’s 1980 with crappy implementations. Maybe because most embedded C compilers are from 1980 and are crappy implementations? It certainly seems like C99 is seem as a fad despite being 25 years old by now …

    Reply
    1. Allen Huffman Post author

      You have no idea. For example, the “windows” compiler we use from National instruments – LabWindows – is still using Clang 3.3. I believe that is up to 19 now.

      If not “boardIdx”, what would you do when looping through a list of control boards or whatever?

      Reply
      1. Sean Patrick Conner

        I would do

        for (size_t i = 0 ; i < MAX ; i++)
        {
        if (board[i].foo == blah) ...
        }

        I just checked, and I tend to use i mostly. Although this loop uses a different name:

        for (int fh = STDERR_FILENO + 1 ; fh <= devnull ; fh++)
        if (close(fh) == -1)
        _Exit(EX_OSERR);

        fh for “file handle” I suppose. So, yes, I seem to go against my “no Hungarian notation” from time to time.

        Reply
          1. Sean Patrick Conner

            Let’s try this again:

            for (size_t i = 0 ; i < MAX ; i++)
            {
            if (board[i].foo == blah) ...
            }

            for (int fh = STDERR_FILENO + 1 ; fh <= devnull ; fh++)
            if (close(fh) == -1)
            _Exit(EX_OSERR);

          2. Allen Huffman Post author

            WordPress is not optimal for code in comments. So many of my early posts here are all messed up, since WP changed to this “block” system (I need to go through every post and redo them).

          3. Sean Patrick Conner

            I can’t seem to reply directly to your question about the fh variable. Yes, it is a file handle for open(), read(), etc.

          4. Allen Huffman Post author

            I am using Visual Studio to compile C right now. Just say an error about strncpy being unsafe with an alternative strncpy_s. Never seen this before, but have written my own! I am really missing out.

        1. Allen Huffman Post author

          Hungarian is like “bufferPtr” or “scoreInt”? Like Microsoft used to do?

          Clean Code stuff seems to say make it say what it is. “i” is neither, so what do we call that type?

          Reply
          1. Sean Patrick Conner

            Yes, stuff like `lpstrFoo’ is Hungarian, from Microsoft employee Charles Simonyi. I’m also not a fan of Uncle Bob nor his “Clean Code” stuff. Probably the best advice I ever got for C coding was from the book Writing Solid Code.

          2. MiaM

            i might not technically fit the descriptions of clean code, but by convention everyone “knows” that i as an integer and also that it’s a loop counter.

          3. MiaM

            Can the code be considered readable if it’s not obvious what a counter counts?

            In particular there might be circumstances where the same counter counts various things simultaneously. Sure, the common case for that would be a while loop rather than an if loop, but still.

          4. Allen Huffman Post author

            My understanding of “clean code” is the goal is the code documents itself.

            DealCard (i);

            That should be obvious. i is the card number to deal, so maybe i is okay. But earlier…

            i = Random(52);

            If that said…

            cardNumber = Random(52);

            Then it becomes obvious what the variable represents.

            But “magic numbers” would have..

            cardNumber = Random(NUMBER_OF_CARDS);

            or something…

            But there is also a goal of small functions that fit on one screen. And if you can see it all, then “i” becomes obvious.

    2. Allen Huffman Post author

      P.S. My PIC24 embedded C compiler has strcpy(), but no strncpy(). For embedded, I’d prefer them to drop the “unsafe” ones and at just provide the ones with length limiters ;-)

      Reply
  3. James Jones

    My BS (degree!) is for math, and I confess to using i and j (never got deep enough to use k or l, and l looks too much like 1 anyway), and m and n for upper bounds.

    For some time C has allowed

    for (int i = 0; i < n; i++) {
    /* i only has this meaning inside this loop */
    }

    which drastically (unless the loop is huge, which it shouldn’t be) cuts down on the space you have to search for a given use.

    cardIdx? That runs the risk of what the golang people call “stuttering”. What are you indexing with cardIdx? I hope the code doesn’t look like card[cardIdx]. If you have two indices, are they cardIdx1 and cardIdx2? That makes it really easy to confuse the two. i1 and i2 are much easier to tell apart. For that matter, cardIdx sounds more like a type than a variable name.

    Reply
    1. Allen Huffman Post author

      Worst is… multiplexer boards that then talk to a series of control boards so I get…

      mux[muxIdx].board[boardIdx]

      Open to suggestions :-) I’m all about changing my style based on these blog comments!

      For anyone reading, anything good I do, is because of James. All the rest, is because of me :-(

      Reply
  4. MiaM

    A problem with a variable name like idx is that it sounds like a loop counter that loops different delta values for x coordinates. I.E. say that you want to draw graphics that simulates a vehicle accelerating horizontally, you would then have a loop that increases the delta-x in order for the x position to move faster and faster horizontally.

    This might be a spicy take, but if you need to search for the name of a loop counter, maybe the loop is too long (written-code-wise, perhaps not execution-wise)?

    Reply
    1. Allen Huffman Post author

      I would happily use x and y for coordinate loops. And I think “card” makes sense but then looks odd with “card[card]” so maybe then I’d use “card[number]”?

      I have learned there can be no set rule since a set rule may become counter intuitive for some types of loops.

      Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.