Solved starting at a specified point in a constant string

Code which tacks on an "s" when a described quantity is not exactly 1:

Code:
#include <stdio.h>
void display_rabbit_count(int the_count)
{
  printf(">>>%d rabbit%s<<<\n",
         the_count,
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstring-plus-int"
         "s"+(the_count==1)        /* <--------- the expression of interest */
#pragma clang diagnostic pop
  );
}
int main(void)
{
  display_rabbit_count(0);
  display_rabbit_count(1);
  display_rabbit_count(2);
  display_rabbit_count(3);
  return 0;
}
The output:
Code:
>>>0 rabbits<<<
>>>1 rabbit<<<
>>>2 rabbits<<<
>>>3 rabbits<<<
The expression of interest points to the empty string for quantity 1, and to the string "s" for all other quantities. To suppress a diagnostic message for this particular expression, clang requires me to use #pragma. Is there any other C expression, similarly concise, which will do the job and help me avoid the warning in the first place? You'd think that I could follow the advice of the diagnostic message and index into the string, and I'd need to use %c, not %s, in the format string, but the case of one rabbit would produce a NUL character in the output, which I wish to avoid.
 
The code you have here is, although perfectly valid, not readable. The only reason I could see for writing it this way is space efficiency. But for that, the following would be much more readable and I doubt there would be a significant difference in the compiled size:
Code:
printf(">>>%d rabbit%s<<<\n", the_count,
        the_count==1 ? "" : "s");
For optimizing execution time, you'd do the ternary on the whole format string instead, getting rid of the %s replacement.

edit: That being said, maybe you have another use case for this construct that has more benefit. To just silence the warning, an explicit cast to const char * will do:
Code:
printf(">>>%d rabbit%s<<<\n", the_count, (const char *)"s"+(the_count == 1));
edit2: Thinking twice, of course array indexing, as suggested by clang, will work as well:
Code:
printf(">>>%d rabbit%s<<<\n", the_count, &"s"[the_count == 1]);
The trick here is to take the address of the result which is, again, char *
 
Just adding, tobik's solution is the best in terms of readability. A ternary is IMHO acceptable, like in my first suggestion above, but clear structured code remains the best. "Dirty" tricks are for embedded platforms, the C64, or any other environment with a really good reason to use them. Or for IOCCC, of course ;)
 
What is, or is not, obfuscation is not always as simple as it seems. I would have thought plain pointer arithmetic:
Code:
"s"+(the_count==1)
would have been simple enough to understand, but our discussion makes it clear that the expression is not intuitively obvious to all readers.

The code I showed in the original post conveys the problem, but not the overall context. The idiom
Code:
"s"+(the_count==1)
appears (with variations) many times in the actual code. Once the reader of the code understands the pointer arithmetic, the remaining instances are quite readable. The reason that I wished for a "similarly concise" expression was to avoid the quite readable if/ then solution presented by tobik; though readable, it would have caused the code to explode in volume. The ternary expression proposed by Zirias would have been much more concise, but the ampersand solution, also mentioned by Zirias, is much more concise and therefore readable when promulgated through the code:
Code:
"s"+(the_count==1)  /* old */
&"s"[the_count==1]  /* new */
What makes that solution slightly less attractive is that it obfuscates the fact that we want to do pointer arithmetic. But I played around and found a solution (that is, suppression of the warning message) which is concise and does not disguise the fact that we're doing pointer arithmetic:
Code:
"s"+(the_count==1)    /* old */
("s")+(the_count==1)  /* new */
 
but our discussion makes it clear that the expression is not intuitively obvious to all readers.
It's obvious to me after a second of thinking what happens here internally. But for professional, production-quality code, this is too much to go without strong reasoning and explaining comment. The goal is most of the time to write code as simple and clear, it doesn't need any further thought to know what it does when you see it. There are exceptions from that, see above ;)

The reason that I wished for a "similarly concise" expression was to avoid the quite readable if/ then solution presented by tobik; though readable, it would have caused the code to explode in volume.
I'd say the canonical solution here is refactoring. Probably just factor it out in a function, depending on the current structure of your code.

What makes that solution slightly less attractive is that it obfuscates the fact that we want to do pointer arithmetic.
How is that? No matter what syntax is used, this kind of code can only be understood when you know that string literals in C are, in fact, pointers to const char. It's bad (hence the warning) because a + operator is used for string concatenations in many languages that look a lot like C, so it easily confuses the casual reader of the code. This confusion is avoided using the index operator, which behaves more similar to other languages, and is well defined on typed pointers in C.
 
Back
Top