char *
itoa(int n)
{
static char out[WIDTH + 1];
for(char *outp = out + WIDTH - 1; outp >= out; --outp, n /= 10)
*outp = '0' + n % 10;
Nice bounds-check (outp >= out). This ensures that you don't overrun your buffer when given a very large integer. However, since out-1 is not a valid pointer value, outp >= out is an incorrect bounds-check for the same reason that
int decr(int x) {
int y = x - 1;
if (y > x) return 0; // oops, overflow happened
return y;
}
is an incorrect overflow-check.
Your bounds-check code also has another similarity to the above overflow-check code: When overflow happens, you return an in-range value that the caller might reasonably confuse for a valid result of the function. When itoa(x) returns "42", does it mean that x was 42, or does it mean that x was 1042 and overflow happened? It would be much better to say, if overflow happens, we return NULL.
Of course you'll have to express that "if" without if. ;)
itoa returns char*, but you don't expect the caller to modify the pointed-to chars, so you should declare it as returning a pointer to non-modifiable chars: const char*. Const is a contract.
Similarly, static char *FBSTR[4] should be static const char *FBSTR[4].
static const char *FBSTR[4] = {"", "Fizz", "Buzz", NULL};
would be much clearer to the human reader; it shows that you didn't miscount the number of array elements. In fact, you might even write
static const char *FBSTR[] = {"", "Fizz", "Buzz", NULL};
although the downside of that is that the number of indices is actually really important to your algorithm, so I can see a valid argument for keeping the 4 in this case.
fputs(FBSTR[1 * (n%3 == 0)], stdout);
fputs(FBSTR[2 * (n%5 == 0)], stdout);
fputs(FBSTR[3 * (n%3 > 0 && n%5 > 0)], stdout);
putchar('\n');
I did puzzle this out, but printing all those empty strings feels like a strange way to go about it. Why not simply
int idx = 1*(n%3 == 0)
+ 2*(n%5 == 0)
+ 3*(n%3 != 0 && n%5 != 0);
fputs(FBSTR[idx], stdout);
putchar('\n');
In which case you can combine the fputs and putchar into
puts(FBSTR[idx]);
If you think it would be easier to understand, you could even rewrite the index computation using bitwise operations:
const char *FBSTR[] = {itoa(n), "Fizz", "Buzz", "FizzBuzz"};
int idx = (n%3 == 0) + 2*(n%5 == 0);
puts(FBSTR[idx]);
To avoid reinventing the wheel, you could replace itoa(n) with snprintf(somelocalbuffer, sizeof somelocalbuffer, "%d", n). However, for this toy example, itoa does have a more ergonomic interface; and reinventing the wheel isn't necessarily a bad thing.