Pregunta

¿Cuál es la necesidad del operador condicional?Funcionalmente es redundante, ya que implementa una construcción if-else.Si el operador condicional es más eficiente que la asignación equivalente if-else, ¿por qué el compilador no puede interpretar if-else de manera más eficiente?

¿Fue útil?

Solución

El operador ternario es una conveniencia y facilidad de lectura sintáctica, no un acceso directo rendimiento. Las personas están divididos sobre los méritos de él para los condicionales de diversa complejidad, pero para condiciones cortos, puede ser útil tener una expresión de una línea.

Por otra parte, ya que es una expresión, como Charlie Martin escribió , eso significa que puede aparecer en el lado derecho de una declaración en C. Esto es valioso por ser conciso.

Otros consejos

En C, la utilidad real de la misma es que se trata de un expresión en lugar de una declaración; es decir, se puede tener en el lado derecho (RHS) de un comunicado. Para que pueda escribir ciertas cosas de manera más concisa.

Algunas de las otras respuestas dadas son grandes. Pero me sorprende que nadie ha mencionado que se puede utilizar para ayudar a hacer cumplir la corrección const de una manera compacta.

Algo como esto:

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

Así que, básicamente n es un const cuyo valor inicial es dependiente de una instrucción de condición. La alternativa más fácil es hacer un n no const, esto permitiría un if ordinaria para inicializarlo. Pero si usted quiere que sea const, no se puede hacer con un if ordinaria. El mejor sustituto que podría hacer sería utilizar una función de ayuda de esta manera:

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

const int n = f(x);

pero el ternario si la versión es mucho más compacto y sin duda más fácil de leer.

Es crucial para la ofuscación de código, por ejemplo:

Look->       See?!

No
:(
Oh, well
);

compacidad y la capacidad de inline un if-then-else constructo en una expresión.

Hay muchas cosas en C que no son necesarios técnicamente, ya que pueden ser más o menos fácilmente implementados en términos de otras cosas. Aquí es una lista incompleta:

  1. mientras
  2. de
  3. funciones
  4. estructuras

Imagine lo que su código se vería como sin ellos y usted puede encontrar su respuesta. El operador ternario es una forma de "azúcar sintáctico" que si se utiliza con cuidado y habilidad hace que la escritura y la comprensión código sea más fácil.

A veces el operador ternario es la mejor manera de realizar el trabajo. En particular, cuando se desea que el resultado de la ternaria ser un l-valor.

Esto no es un buen ejemplo, pero estoy un dibujo en blanco en somethign mejor. Una cosa es certian, no es a menudo cuando realmente es necesario utilizar el ternario, aunque todavía lo uso bastante.

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

Una cosa que me gustaría advertir contra ternarios aunque se ensartar juntos. Se convierten en un verdadero
problema a la hora de maintennance:

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

Editar : He aquí un ejemplo potencialmente mejor. Se puede utilizar el operador ternario para asignar referencias y valores const donde de otro modo debería escribir una función para manejarlo:

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

const int myValue = getMyValue();

... podría llegar a ser:

const int myValue = myCondition ? 42 : 314;

¿Qué es mejor es una cuestión discutible que voy a optar por no debatir.

Dado que nadie ha mencionado esto, sin embargo, la única manera de obtener declaraciones printf inteligentes es utilizar el operador ternario:

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

Advertencia: Hay algunas diferencias en la prioridad de los operadores cuando se mueve de C a C ++ y puede ser sorprendido por el insecto (s) sutil que surgen de los mismos

.

El hecho de que el operador ternario es una expresión, no una declaración, permite que sea utilizado en las expansiones de macro para macros de función como que se utilizan como parte de una expresión. Const puede no haber sido parte de C original, pero el pre-procesador de macros va camino de vuelta.

Uno de los lugares en los que he visto que se usa es en un paquete matriz que utiliza macros para los accesos matriz de cota marcada. La sintaxis para una referencia comprobado era algo así como aref(arrayname, type, index), donde arrayname era en realidad un puntero a una estructura que incluía los límites de la matriz y una matriz de caracteres sin signo de los datos, el tipo era el tipo real de los datos, y el índice fue el índice. La expansión de este era bastante peluda (y no voy a hacerlo desde la memoria), pero utiliza algunos operadores ternarios que hacer la revisión con destino.

No se puede hacer esto como una llamada a la función en C debido a la necesidad de que el polimorfismo del objeto devuelto. Así que se necesitaba una macro para hacer la conversión de tipos en la expresión. En C ++ se podría hacer esto como una llamada de función sobrecargada con plantilla (probablemente para el operador []), pero C no tiene tales características.

Edit: Aquí está el ejemplo que estaba hablando, desde el paquete de CAD gama de Berkeley (glu 1.4 edición). La documentación del uso array_fetch es:

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

Obtener un elemento de una matriz. UNA   error de ejecución se produce en un intento de   referencia fuera de los límites de la   formación. No hay ningún tipo de comprobación   de que el valor en la posición dada   en realidad es del tipo utilizado cuando   dereferencing la matriz.

y aquí está la Defintion macro de array_fetch (nótese el uso del operador ternario y el operador secuenciación coma para ejecutar todas las subexpresiones con los valores correctos en el orden correcto como parte de una sola expresión):

#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)))

La expansión para array_insert (que crece la matriz, si es necesario, como un vector C ++) es aún más peludo, que implica múltiples operadores ternarios anidados.

Su azúcar sintáctico y una abreviatura útil para breve if / else bloques que sólo contienen un comunicado. Funcionalmente, ambas construcciones deben realizar de forma idéntica.

operador ternario puede ser de más rendimiento que una normal si cláusula else, esto puede ser crítico en aplicaciones embebidas, sino también la optimización del compilador puede colapsar esta diferencia.

como DWN dijo, rendimiento fue uno de sus beneficios durante el auge de los procesadores complejos, el blog de MSDN procesador de comportamiento no clásico:. ¿Cómo hacer algo puede ser más rápido que el no hacerlo da un ejemplo que dice claramente la diferencia entre ternaria operador (condicional) y si / else

dar el siguiente 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;
}

el costo para diferentes límites son muy diferentes y extraño (ver el material original). mientras que si el cambio:

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

a

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

El tiempo de ejecución es ahora independiente del valor límite, ya que:

  

el optimizador fue capaz de eliminar la rama de la expresión ternaria.

pero en mi escritorio Intel i5 CPU / Windows 10 / vs2015, resultado de la prueba es bastante diferente con el blog de MSDN.

cuando se utiliza el modo de depuración , si el costo / 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

y el costo operador ternario:

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 

cuando se utiliza el modo de disparo , si el costo / else:

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

y el costo operador ternario:

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

el operador ternario es más lento que si / else en mi máquina!

lo que de acuerdo a las diferentes técnicas de optimización del compilador, operador de terno y si / else puede comporta de forma muy diferente.

ternario = forma simple de si-else. Está disponible sobre todo para facilitar la lectura.

  • Algunos de los operadores más oscuros en C existen únicamente porque permiten la implementación de varias macros similares a funciones como una única expresión que devuelve un resultado.Yo diría que este es el objetivo principal por el cual ?: y , Los operadores pueden existir, aunque su funcionalidad sea redundante.

    Digamos que deseamos implementar una macro similar a una función que devuelva el mayor de dos parámetros.Entonces se llamaría así por ejemplo:

    int x = LARGEST(1,2);
    

    La única forma de implementar esto como una macro similar a una función sería

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

    No sería posible con un if ... else declaración, ya que no devuelve un valor de resultado. Nota)

  • El otro propósito de ?: es que en algunos casos en realidad aumenta la legibilidad.Más amenudo if...else es más legible, pero no siempre.Tomemos, por ejemplo, declaraciones de cambio largas y 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;
      ...
    }
    

    Esto se puede reemplazar con el mucho más legible.

    switch(something)
    {
      case A: array[i] = (x == A) ? x : y; break;
      case B: array[i] = (x == B) ? x : y; break;
      ...
    }
    
  • Tenga en cuenta que ?: hace nunca resulta en un código más rápido que if-else.Se trata de un extraño mito creado por principiantes confundidos.En caso de código optimizado, ?: ofrece un rendimiento idéntico al if-else en la gran mayoría de los casos.

    Si algo, ?: puede ser Más lento que if-else, porque viene con promociones de tipo implícitas obligatorias, incluso del operando que no se va a utilizar.Pero ?: nunca puede ser más rápido que if-else.


Nota) Ahora, por supuesto, alguien discutirá y se preguntará por qué no utilizar una función.De hecho, si puedes usar una función, es siempre preferible a una macro similar a una función.Pero a veces no puedes usar funciones.Supongamos por ejemplo que x en el ejemplo anterior se declara en el ámbito del archivo.Entonces, el inicializador debe ser una expresión constante, por lo que no puede contener una llamada a función.Otros ejemplos prácticos de dónde debe utilizar macros similares a funciones implican la programación segura con tipos _Generic o "X macros".

El mismo que

if(0)
do();


if(0)
{
do();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top