Тернарный (условный) оператор в C - PullRequest
52 голосов
/ 17 апреля 2009

Зачем нужен условный оператор? Функционально это избыточно, поскольку оно реализует конструкцию if-else. Если условный оператор более эффективен, чем эквивалентное присваивание if-else, почему компилятор не может интерпретировать if-else более эффективно?

Ответы [ 15 ]

2 голосов
/ 17 ноября 2012

Тернарный оператор может иметь более высокую производительность, чем обычный оператор if, это может быть критично во встроенных приложениях, но оптимизация компилятора также может сократить эту разницу.

1 голос
/ 22 апреля 2017

Как сказал dwn, производительность была одним из ее преимуществ во времена появления сложных процессоров, блог MSDN Не классическое поведение процессора: как что-то сделать быстрее, чем не делать приводит пример, который ясно разница между троичным (условным) оператором и оператором if / else.

введите следующий код:

#include <windows.h>
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>

int array[10000];

int countthem(int boundary)
{
 int count = 0;
 for (int i = 0; i < 10000; i++) {
  if (array[i] < boundary) count++;
 }
 return count;
}

int __cdecl wmain(int, wchar_t **)
{
 for (int i = 0; i < 10000; i++) array[i] = rand() % 10;

 for (int boundary = 0; boundary <= 10; boundary++) {
  LARGE_INTEGER liStart, liEnd;
  QueryPerformanceCounter(&liStart);

  int count = 0;
  for (int iterations = 0; iterations < 100; iterations++) {
   count += countthem(boundary);
  }

  QueryPerformanceCounter(&liEnd);
  printf("count=%7d, time = %I64d\n",
         count, liEnd.QuadPart - liStart.QuadPart);
 }
 return 0;
}

стоимость за разные границы сильно отличается и странна (см. Оригинальный материал). пока если поменяю:

 if (array[i] < boundary) count++;

до

 count += (array[i] < boundary) ? 1 : 0;

Время выполнения теперь не зависит от граничного значения, поскольку:

оптимизатору удалось удалить ветку из троичного выражения.

но на моем настольном компьютере Intel i5 cpu / Windows 10 / vs2015 результаты моего теста в блоге msdn совсем другие.

при использовании режима отладки , если / else стоимость:

count=      0, time = 6434
count= 100000, time = 7652
count= 200800, time = 10124
count= 300200, time = 12820
count= 403100, time = 15566
count= 497400, time = 16911
count= 602900, time = 15999
count= 700700, time = 12997
count= 797500, time = 11465
count= 902500, time = 7619
count=1000000, time = 6429

и стоимость троичного оператора:

count=      0, time = 7045
count= 100000, time = 10194
count= 200800, time = 12080
count= 300200, time = 15007
count= 403100, time = 18519
count= 497400, time = 20957
count= 602900, time = 17851
count= 700700, time = 14593
count= 797500, time = 12390
count= 902500, time = 9283
count=1000000, time = 7020 

при использовании режима разблокировки , если / еще стоимость:

count=      0, time = 7
count= 100000, time = 9
count= 200800, time = 9
count= 300200, time = 9
count= 403100, time = 9
count= 497400, time = 8
count= 602900, time = 7
count= 700700, time = 7
count= 797500, time = 10
count= 902500, time = 7
count=1000000, time = 7

и стоимость троичного оператора:

count=      0, time = 16
count= 100000, time = 17
count= 200800, time = 18
count= 300200, time = 16
count= 403100, time = 22
count= 497400, time = 16
count= 602900, time = 16
count= 700700, time = 15
count= 797500, time = 15
count= 902500, time = 16
count=1000000, time = 16

троичный оператор работает медленнее, чем оператор if / else на моей машине!

так, в соответствии с различными методами оптимизации компилятора, оператор ternal и if / else могут вести себя очень по-разному.

0 голосов
/ 25 января 2018
  • Некоторые из более неясных операторов в C существуют исключительно потому, что они допускают реализацию различных функционально-подобных макросов в виде единого выражения, которое возвращает результат. Я бы сказал, что это основная цель, по которой операторам ?: и , разрешено существовать, даже если их функциональность в противном случае избыточна.

    Допустим, мы хотим реализовать подобный функции макрос, который возвращает наибольший из двух параметров. Тогда он будет назван как, например:

    int x = LARGEST(1,2);
    

    Единственный способ реализовать это как функциональный макрос - это

    #define LARGEST(x,y) ((x) > (y) ? (x) : (y))
    

    Это невозможно сделать с помощью оператора if ... else, поскольку он не возвращает значение результата. Примечание)

  • Другая цель ?: состоит в том, что он в некоторых случаях действительно увеличивает читабельность. Чаще всего if...else более читабельно, но не всегда. Возьмем, к примеру, длинные повторяющиеся операторы переключения:

    switch(something)
    {
      case A: 
        if(x == A)
        {
          array[i] = x;
        }
        else
        {
          array[i] = y;
        }
        break;
    
      case B: 
        if(x == B)
        {
          array[i] = x;
        }
        else
        {
          array[i] = y;
        }
        break;
      ...
    }
    

    Это можно заменить гораздо более читабельным

    switch(something)
    {
      case A: array[i] = (x == A) ? x : y; break;
      case B: array[i] = (x == B) ? x : y; break;
      ...
    }
    
  • Обратите внимание, что ?: не делает никогда приводит к более быстрому коду, чем if-else. Это какой-то странный миф, созданный смущенными новичками. В случае оптимизированного кода ?: дает такую ​​же производительность, что и if-else в подавляющем большинстве случаев.

    Во всяком случае, ?: может быть на медленнее , чем if-else, потому что он идет с обязательными неявными повышениями типа, даже операнда, который не будет использоваться. Но ?: никогда не может быть быстрее, чем if-else.


Примечание) Теперь, конечно, кто-то поспорит и удивится, почему бы не использовать функцию. В самом деле, если вы можете использовать функцию, она всегда предпочтительнее функционального макроса. Но иногда вы не можете использовать функции. Предположим, например, что x в приведенном выше примере объявлено в области видимости файла. Тогда инициализатор должен быть константным выражением, поэтому он не может содержать вызов функции. Другие практические примеры использования функционально-подобных макросов включают программирование безопасного типа с _Generic или "X-макросами".

0 голосов
/ 24 октября 2009

Так же, как

if(0)
do();


if(0)
{
do();
}
0 голосов
/ 17 апреля 2009

троичный = простая форма if-else. Он доступен в основном для удобства чтения.

...