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:

25 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,0x8(%ebp)
    9: 8b 45 14 mov 0x14(%ebp),%eax
    c: 0f 4f 45 10 cmovg 0x10(%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,0x8(%ebp)
    1b: 8b 45 14 mov 0x14(%ebp),%eax
    1e: 0f 4f 45 10 cmovg 0x10(%ebp),%eax
    22: 5d pop %ebp
    23: c3 ret

  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.

  17. BTL says:

    The real reason not to use the ternary operator is because it causes maintenance/readability issues and it isn’t compliant with MISRA-C rules. I would MUCH rather have the 8 line if/else block spelled out than a single line. LOC doesn’t matter anyway – architect your modules so that you don’t end up with 1000’s of lines in a file and you’re fine. LOC is not a good measure of code quality, one way or the other. Ternary operations are generally used by programmers who think they are especially clever and generally they try to do other “tricks” in their code that end up being a maintenance nightmare. Simple code and first-glance-readable code will always be better than tricks and clever solutions from a long term perspective.

  18. MAF says:

    Some programmers use ternary operators for side effects like so:

    (x < 0)?(x–):(x==0);

    This is a MISRA violation, I think, and hard to read.

  19. Brendan Simon says:

    The issue is that some compilers are bad at optimising ternary operators, or optimisation is disabled.

    That is a choice of tool issue. If the tool is crap, get better tools.

    Yes, you can work around by trying different things, but you shouldn’t have to. If it makes sense to use a ternary (e.g. if it makes the code simpler and easier to understand) then by all means use it. If the compiler gets in the way then get an upgrade or change compilers.

    If you don’t push back on the vendor you wont see any progress 🙂

  20. Tony Mach says:

    I just had a case where GCC 4.4.7 for AVR32 with the -Os optimizer setting created more effective code (actually elegant code) for a ternary operator (compared with the same code with if/else).

    The relevant C code (from LWIP) is this:

    #define ip4_addr_set(dest, src) ((dest)->addr = ((src) == NULL ? 0 : (src)->addr))

    Ternary operator:

    cp.w r5,0
    ld.wne r5,r5[0x0]
    st.w r7[0x8],r5

    Rewritten as If/Else:

    cp.w r5,0
    ld.wne r8,r5[0x0]
    st.wne r7[0xc],r8
    st.weq r7[0xc],r5

    The only problem? GCC does on certain conditions create the wrong code for the ternary operator…

    cp.w r5,0
    ld.wne r5,r5[0x0]
    st.w r7[0xc],r9

    Find the bug!

  21. Thanks for this information.This is second time i am reading this.

Leave a Reply to Nigel Jones

You must be logged in to post a comment.