Pergunta

O que é a necessidade do operador condicional? Funcionalmente, é redundante, uma vez que implementa uma construção if-else. Se o operador condicional é mais eficiente que a atribuição equivalente if-else, por que não if-else ser interpretada de forma mais eficiente pelo compilador?

Foi útil?

Solução

O operador ternário é uma conveniência sintática e legibilidade, não um atalho desempenho. As pessoas estão divididos sobre os méritos de que para condicionais de complexidade variável, mas para condições de curto, pode ser útil ter uma expressão de uma linha.

Além disso, uma vez que é uma expressão, como Charlie Martin escreveu , isso significa que ele pode aparecer no lado direito de uma declaração em C. Isso é importante por ser conciso.

Outras dicas

Em C, a real utilidade do que é que é uma expressão em vez de uma declaração; isto é, você pode tê-lo no lado direito (RHS) de um comunicado. Então você pode escrever certas coisas de forma mais concisa.

Algumas das outras respostas dadas são grandes. Mas eu estou surpreso que ninguém mencionou que ele pode ser usado para ajudar a impor const correção de uma forma compacta.

Algo parecido com isto:

const int n = (x != 0) ? 10 : 20;

Então, basicamente n é um const , cujo valor inicial é dependente de uma declaração condição. A alternativa mais fácil é fazer n não um const, isso permitiria que um if ordinária para inicializar-lo. Mas se você quer que ele seja const, ele não pode ser feito com um if comum. O melhor substituto que você poderia fazer seria usar uma função auxiliar como este:

int f(int x) {
    if(x != 0) { return 10; } else { return 20; }
}

const int n = f(x);

mas o ternário se a versão é muito mais compacto e sem dúvida mais legível.

É crucial para ofuscação de código, como este:

Look->       See?!

No
:(
Oh, well
);

compacidade e a capacidade para inline uma construção if-then-else em uma expressão.

Há um monte de coisas em C que não são tecnicamente necessários, porque eles podem ser mais ou menos facilmente implementado em termos de outras coisas. Aqui está uma lista incompleta:

  1. enquanto
  2. para
  3. funções
  4. estruturas

Imagine o seu código ficaria sem estes e você pode encontrar a sua resposta. O operador ternário é uma forma de "açúcar sintático" que, se usado com cuidado e habilidade torna a escrita e código de compreensão mais fácil.

Às vezes, o operador ternário é a melhor maneira de começar o trabalho feito. Em particular quando você quer o resultado do ternário a ser um l-valor.

Este não é um bom exemplo, mas estou um desenho em branco no somethign melhor. Uma coisa é certian, não é muitas vezes quando você realmente precisa usar o ternário, embora eu ainda usá-lo um pouco.

const char* appTitle  = amDebugging ? "DEBUG App 1.0" : "App v 1.0";

Uma coisa que gostaria de advertir contra o que está amarrando ternários juntos. Eles se tornam um verdadeiro
problema no momento maintennance:

int myVal = aIsTrue ? aVal : bIsTrue ? bVal : cIsTrue ? cVal : dVal;

Editar : Aqui está um exemplo potencialmente melhor. Você pode usar o operador ternário para referências atribuir e valores const onde você se não precisa escrever uma função para lidar com isso:

int getMyValue()
{
  if( myCondition )
    return 42;
  else
    return 314;
}

const int myValue = getMyValue();

... poderia tornar-se:

const int myValue = myCondition ? 42 : 314;

O que é melhor é uma questão discutível que vou optar por não debate.

Uma vez que ninguém mencionou isso ainda, sobre a única maneira de obter declarações printf inteligentes é usar o operador ternário:

printf("%d item%s", count, count > 1 ? "s\n" : "\n");

Advertência: Existem algumas diferenças na precedência do operador quando se desloca de C para C ++ e pode ser surpreendido pelo bug sutil (s) que se levantam

.

O fato de que o operador ternário é uma expressão, não uma declaração, permite que ele seja usado em expansões de macro para a função semelhante macros que são usados ??como parte de uma expressão. Const não pode ter sido parte de C original, mas a macro pré-processador vai caminho de volta.

Um lugar onde eu vi isso usado é em um pacote matriz que macros usados ??para acessos de matriz-bound verificados. A sintaxe para uma referência marcada era algo como aref(arrayname, type, index), onde arrayname foi, na verdade, um ponteiro para uma estrutura que inclui os limites da matriz e uma matriz de char não assinado para os dados, tipo era o tipo real de dados, e o índice era o índice. A expansão desta era muito peludo (e eu não vou fazê-lo a partir da memória), mas usado alguns operadores ternários para fazer a verificação vinculado.

Você não pode fazer isso como uma chamada de função em C por causa da necessidade de polimorfismo do objeto retornado. Assim, uma macro foi necessário para fazer o tipo de vazamento na expressão. Em C ++ você poderia fazer isso como uma chamada de função sobrecarregada templated (provavelmente para o operador []), mas C não tem tais características.

Edit: Aqui está o exemplo que eu estava falando, a partir do pacote de série Berkeley CAD (glu 1,4 edição). A documentação do uso array_fetch é:

type
array_fetch(type, array, position)
typeof type;
array_t *array;
int position;

Obter um elemento de uma matriz. UMA erro de execução ocorre em uma tentativa de referência fora dos limites da matriz. Não há nenhum tipo de verificação que o valor na posição determinada é, na verdade, do tipo usado quando dereferencing matriz.

e aqui é o defintion macro de array_fetch (note o uso do operador ternário e o operador vírgula sequenciamento para executar todas as subexpressions com os valores certos na ordem certa como parte de uma única expressão):

#define array_fetch(type, a, i)         \
(array_global_index = (i),              \
  (array_global_index >= (a)->num) ? array_abort((a),1) : 0,\
  *((type *) ((a)->space + array_global_index * (a)->obj_size)))

A expansão para array_insert (que cresce a matriz, se necessário, como um vector de C ++) é ainda mais peludos, envolvendo múltiplos operadores ternários aninhados.

É de açúcar sintático e um atalho útil para breve if / else blocos que contêm apenas uma instrução. Funcionalmente, ambos os construtos deve executar de forma idêntica.

operador ternário pode ser de mais desempenho do que uma normal se cláusula else, isso pode ser crítico em aplicações embarcadas, mas também de otimização do compilador pode entrar em colapso essa diferença.

como dwn disse, o desempenho foi um dos seus benefícios durante a ascensão de processadores complexos, MSDN blogue comportamento processador não-clássica: Como fazer algo pode ser mais rápido do que não fazê-lo dá um exemplo que diz claramente a diferença entre ternário operador (condicional) e if / else

dar o seguinte código:

#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;
}

o custo de fronteira diferente são muito diferente e estranho (ver o material original). enquanto que se a mudança:

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

para

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

O tempo de execução é agora independente do valor limite, desde que:

o otimizador foi capaz de remover o ramo da expressão ternária.

mas na minha Intel Desktop i5 cpu / windows 10 / vs2015, meu resultado do teste é bastante diferente com blogue MSDN.

quando utilizar o modo de depuração , se / custo mais:

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

e custo operador ternário:

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 

quando utilizar o modo de liberação , se / custo mais:

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

e custo operador ternário:

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

o operador ternário é mais lento do que if / else em minha máquina!

tão de acordo com diferentes técnicas de otimização do compilador, operador terna e if / else se comporta maio muito diferente.

ternário = simples forma de if-else. Ele está disponível principalmente para facilitar a leitura.

  • Alguns dos operadores mais obscuros em C existem apenas porque eles permitem a execução de várias funções-como macros como uma única expressão que retorna um resultado. Eu diria que este é o principal objetivo por que os operadores ?: e , estão autorizados a existir, embora a sua funcionalidade é outra forma redundante.

    Vamos dizer que queremos implementar uma função semelhante a macro que retorna o maior dos dois parâmetros. Seria, então, ser chamado como por exemplo:

    int x = LARGEST(1,2);
    

    A única maneira de implementar esta como uma função semelhante macro seria

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

    Não seria possível com uma declaração if ... else, uma vez que não retorna um valor de resultado. Nota)

  • O outro objetivo de ?: é que em alguns casos, na verdade aumenta a legibilidade. Na maioria das vezes if...else é mais legível, mas nem sempre. Tomemos por exemplo longa, declarações switch repetitivas:

    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;
      ...
    }
    

    Isto pode ser substituído com o muito mais legível

    switch(something)
    {
      case A: array[i] = (x == A) ? x : y; break;
      case B: array[i] = (x == B) ? x : y; break;
      ...
    }
    
  • Por favor note que ?: faz não resultado em código mais rápido do que if-else. Isso é um mito estranho criado por iniciantes confusos. Em caso de código otimizado, ?: dá um desempenho idêntico ao if-else na grande maioria dos casos.

    Se qualquer coisa, ?: pode ser mais lento do que if-else, porque ele vem com promoções do tipo implícito obrigatórias, mesmo do operando que não vai ser utilizado. Mas ?: nunca pode ser mais rápido do que if-else.


Nota) Agora é claro que alguém vai argumentar e maravilha por que não usar uma função. De fato, se você pode usar uma função, é sempre preferível a uma função semelhante a macro. Mas às vezes você não pode usar funções. Suponha, por exemplo, que x no exemplo acima é declarada no escopo do arquivo. O inicializador deve, então, ser uma expressão constante, por isso não pode conter uma chamada de função. Outros exemplos práticos de onde você tem que usar a função semelhante macros envolvem tipo de programação segura, com _Generic ou "macros X".

O mesmo que

if(0)
do();


if(0)
{
do();
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top