8

Okay, I've seen many posts here about odd idioms and common practices in C that might not be initially intuitive. Perhaps a few examples are in order

Elements in an array:

#define ELEMENTS(x) (sizeof (x) / sizeof (*(x)))

Odd array indexing:

a[5] = 5[a]

Single line if/else/while/for safe #defines

#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) { f(X); g(X); } else

My question to the expert C programmers out there is: What idioms, practices, code snippits, or little known facts show up a lot in C code but might not be very intuitive but offer a good insight into C programming?

Community
  • 1
  • 1
Scott
  • 10,407
  • 13
  • 37
  • 35

3 Answers3

12

The "arrow operator" for counting down from n-1 to 0:

for ( int i = n; i --> 0; ) ...

It's not hugely common, but it's an interesting illustration that in some ways the initialize/test/update parts of a for loop are convention. That's the usual pattern, but you can still put any arbitrary expressions in there.

It's also a nice little reminder about how lexical analysis works.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Boojum
  • 6,592
  • 1
  • 30
  • 34
  • http://cplusplus.co.il/2009/12/28/the-omnipotent-arrow-operator/ – rmn Dec 29 '09 at 17:56
  • There is simply no escape from this mighty operator. – GManNickG Dec 29 '09 at 18:07
  • Good for linear search. After all, the first place you should look is the last place you put something. – Samuel Danielson Dec 29 '15 at 20:54
  • I see that this is still here with my sole single downvote. Despite this answer's popularity, I downvoted it for several reasons, not the least of which is you never explained how it works, or why one would use it given the availability of more orthodox, less obscure, and more easily understandable code. – Robert Harvey May 30 '21 at 15:14
7

Since someone will mention it anyway, it might as well be me: Duff's Device.

It's a nice illustration of the way labels work in C, and understanding it gave me an "aha experience" the first time. This is his original code:

send(to, from, count)
register short *to, *from;
register count;
{
    register n=(count+7)/8;
    switch(count%8){
    case 0: do{ *to = *from++;
    case 7:     *to = *from++;
    case 6:     *to = *from++;
    case 5:     *to = *from++;
    case 4:     *to = *from++;
    case 3:     *to = *from++;
    case 2:     *to = *from++;
    case 1:     *to = *from++;
        }while(--n>0);
    }
}

Today, one wouldn't use register, and avoid old-style function definitions.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • 1
    Also, as I understand it, you don't want to do this on a modern optimizing compiler---you'll just be getting in its way. – dubiousjim Apr 03 '10 at 20:13
  • @dubiousjim I wouldn't think so, if it's a memory-mapped port. A modern optimizing compiler probably would make all these copies into one. – S.S. Anne Nov 16 '19 at 22:41
5

The comma operator, while perfectly well documented (K&R etc.) appears in quite a lot of algorithmic code and is often a surprise to many programmers who have not encountered it before. It is often used to simplify some loop constructs:

#define kITERATIONS_MAX 10
int i=0,j=0,array[kITERATIONS_MAX],outArray[kITERATIONS_MAX],temp=0;

for (i=0,j=kITERATIONS_MAX-1; i < kITERATIONS_MAX; i++,j--)
{
temp = array[i]*array[j];
outArray[i]=temp;
}

The above code will multiply array elements 0 thru 9 with 9 thru 0 while avoiding nested loops.

When using the comma operator, both the first and second expressions are evaluated. The result of the first expression is ignored and the result of the second expression is returned.

mikecsh
  • 852
  • 7
  • 12