embedded software boot camp

Efficient C Tips #6 – Don’t use the ternary operator

Wednesday, February 18th, 2009 by Nigel Jones

I have to confess that I like the ternary operator. K&R obviously liked it, as it is heavily featured in their seminal work. However after running experiments on a wide range of compilers I have concluded that with the optimizer turned on, you are better off with a simple if-else statement. Thus next time you write something like this:

y = (a > b) ? c : d;

be aware that as inelegant as it is in comparison, this will usually compile to better code:

if (a > b)
{
 y = c;
}
else
{
 y = d;
}

I find this frustrating, as I’ve consumed 8 lines doing what is more easily and elegantly performed in 1 line.

I can’t say that I have any particular insight as to why the ternary operator performs so poorly. Perhaps if there is a compiler writer out there, they could throw some light on the matter?

Next Tip
Previous Tip
Home

Tags:

19 Responses to “Efficient C Tips #6 – Don’t use the ternary operator”

  1. Uhmmmm says:

    I just did a quick test with gcc 4.3.3, with a function that just does "return (a > b) ? c : d;" and another one with the equivalent if/else. It generates identical assembly for me on both amd64 and arm.

  2. Nigel Jones says:

    I’m not surprised. While I have seen compilers generate the same code for both the ternary operator and the if/else construct, I have never seen a case where the compiler generates better code for the ternary operator. Conversely, I have seen cases where the compiler generates better code for the if/else construct than it does for the ternary operator.

  3. victorh says:

    On some platforms, this form is even more efficient:y = d;if (a > b) { y = c;}provided that y is a regular RAM-based variable. If not, e.g. it is a memory-mapped UART transmit data register, the double assignment could (and will) have disastrous side effects.Use with caution.

  4. Brian Egge says:

    I've found the ternary operator creates the same assembly as the control structure. I think with modern, optimizing compilers, it really a matter of syntaxtic preference.

  5. Nigel Jones says:

    I guess I'll just point to my previous comment. I would add, that as compilers improve (even cheap ones) this should become less of an issue. Indeed, unless I'm writing a time critical piece of code I'll use the operator that more naturally fits the problem.

  6. Mike says:

    Just ran a test with HC08 and CodeWarrior — ternary operator uses 4 more bytes than if/else in one particular test case.

  7. ashleigh says:

    I've seen the same as Nigel – often the ternary operator is worse.I still use it where it improves readability of the source and I don't care about performance.I guess the lesson is – think about what you write. Write with maintenance, readability AND performance in mind. Know what the compiler does (ie go in with your eyes open), and then write whats best for the situation you are handling.

  8. bandit says:

    I use them usually only for printf():printf( "%s", (bletch ? "foo" : "bar") );otherwise I use if() else for clarity. I normally dofoo = 0;if( bar ) foo = 1;because this handles the default case && the exceptional case.

  9. Greg Nelson says:

    The printf() comment reminds me of something else… Nigel, since you said you are often interested in people's weird coding conventions, this one might fit the bill. Want to guess why I write:char tmp1[10];char tmp2[10];char tmp3[10];char final[50];snprintf(tmp1, "%7.1f", f1);snprintf(tmp2, "%7.1f", f2);snprintf(tmp3, "%7.1f", f3);snprintf(final, "%s %s %s", tmp1, tmp2, tmp3);Answer:Because one of the compilers/libraries (still haven't found the culprit) used for our code can't handle multiple double arguments to a varargs call!If I do it the conventional way with "%7.1f %7.1f %7.1f" instead, I either get garbage output, or the program hangs.My guess is this has something to do with poor floating point emulation and/or different ways of stacking doubles versus ints/ptrs/etc.

  10. Doug says:

    My personal favorite “do not use unless absolutely necessary” construct is modulo. Modulo looks simple and elegant when written, but try it out — write a simple modulo construct, compile it and look at the assembly output. It’s horrific. Think the problem through and simplify it instead. I haven’t yet found a modulo solution I couldn’t implement much more simply and completely by rethinking the problem, resulting in code that’s as accurate, uses less memory, and runs quicker.

  11. Peter says:

    Hello Nigel,
    Interesting post. Can you add some examples of code generated for the ternary operator and for the if/else construct, for comparison?

    That would make the post more complete.
    Thanks!

  12. Jill says:

    “I find this frustrating, as I’ve consumed 8 lines doing what is more easily and elegantly performed in 1 line.”

    You mean you’ve expressed more clearly in 4 lines what you could have obfuscated in 1 line.

  13. Jörg Seebohn says:

    I’ve disassembled the generated object module from the following C-module constisting only of these
    two functions. The compiler is gcc 4.5.2 on a x86 processor.

    int f1(int a, int b, int c, int d)
    {
    int y ;
    y = (a > b) ? c : d ;
    return y ;
    }

    int f2(int a, int b, int c, int d)
    {
    int y ;
    if (a > b) { y = c ; } else { y = d ; }
    return y ;
    }

    The generated assembler code is exactly the same for both functions.
    Even better cause of the CMOVcc instruction of the x86 processor the
    generated code is branchless.

    Disassembly of section .text:

    00000000 :
    0: 55 push %ebp
    1: 89 e5 mov %esp,%ebp
    3: 8b 45 0c mov 0xc(%ebp),%eax
    6: 39 45 08 cmp %eax,0×8(%ebp)
    9: 8b 45 14 mov 0×14(%ebp),%eax
    c: 0f 4f 45 10 cmovg 0×10(%ebp),%eax
    10: 5d pop %ebp
    11: c3 ret

    00000012 :
    12: 55 push %ebp
    13: 89 e5 mov %esp,%ebp
    15: 8b 45 0c mov 0xc(%ebp),%eax
    18: 39 45 08 cmp %eax,0×8(%ebp)
    1b: 8b 45 14 mov 0×14(%ebp),%eax
    1e: 0f 4f 45 10 cmovg 0×10(%ebp),%eax
    22: 5d pop %ebp
    23: c3 ret

    • Nigel Jones says:

      Yes. I have come across plenty of compilers that perform as gcc has done here. However I have also come across a number of compilers, particularly for low end embedded systems where this is *not* the case.

  14. Andi says:

    After reading your post, I’m just wondering why compilers treat a ternary operator different then a simple “if() {} else {}” at all ?

    I always thought of the ternary operator as just a more compact form of an “if” statement, with only the added benifit that one can use them in an initializer …

  15. Rob says:

    The ternary operator is a foul, ugly beast and should have been shot at birth!

  16. lazureus says:

    Hi,

    I’m reading this post and I got some mixed feelings about this tip. Ternary operator is very comfortable and I use it quite often for the construction with single thing to do inside the if or else body. I always thought that there is no difference between if … else construct and ternary and now I’m a little bit confused. I read also some manuals how to write efficient code and in none of them was such a claim.
    @Nigel, are you able to give us example of the compilers which generate actually worse code with using of ternary operator ? I’ll begin some testing with compilers which I got currently namely gcc, arm-none-eabi-gcc, clang , ghs.

    • Budz says:

      In my application, on an 8bit microcontroller using gcc, ternary saves me one line in assembly over if..else.. so it is definately platform dependant. My case it was being used in an interrupt routine, and speed is my friend. IMO it is also much easier to read, in my situation of course.

Leave a Reply