El operador ternario(condicional) en C
operador ternario php (15)
A veces, el operador ternario es la mejor manera de hacer el trabajo. En particular, cuando quiere que el resultado del ternario sea un valor l.
Este no es un buen ejemplo, pero me estoy quedando en blanco en algo mejor. Una cosa es cierta, no es frecuente cuando realmente necesitas usar el ternario, aunque todavía lo uso bastante.
const char* appTitle = amDebugging ? "DEBUG App 1.0" : "App v 1.0";
Sin embargo, una cosa contra la que advertiría es unir ternaries. Se vuelven reales
problema en el tiempo de mantenimiento:
int myVal = aIsTrue ? aVal : bIsTrue ? bVal : cIsTrue ? cVal : dVal;
EDITAR : Aquí hay un ejemplo potencialmente mejor. Puede usar el operador ternario para asignar referencias y valores const donde, de otro modo, tendría que escribir una función para manejarlo:
int getMyValue()
{
if( myCondition )
return 42;
else
return 314;
}
const int myValue = getMyValue();
...podría convertirse:
const int myValue = myCondition ? 42 : 314;
Lo que es mejor es una pregunta discutible que elegiré no debatir.
¿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 if-else equivalente, ¿por qué el compilador no puede interpretar if-else de manera más eficiente?
Como nadie ha mencionado esto todavía, la única forma de obtener declaraciones de printf
inteligentes es usar el operador ternario:
printf("%d item%s", count, count > 1 ? "s/n" : "/n");
Advertencia: hay algunas diferencias en la precedencia del operador cuando se mueve de C a C ++ y puede sorprenderse por los errores sutiles que surgen de la misma.
Compacidad y la capacidad de alinear un constructo if-then-else en una expresión.
El hecho de que el operador ternario sea una expresión, no un enunciado, le permite ser utilizado en macroexpansiones para macros similares a funciones que se usan como parte de una expresión. Es posible que Const no haya formado parte de C original, pero el preprocesador macro se remonta a mucho tiempo atrás.
Un lugar donde lo he visto utilizado es en un paquete de matriz que usaba macros para accesos de matriz verificados. La sintaxis para una referencia comprobada 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 char sin signo para los datos, tipo era el tipo real de los datos, e índice fue el índice. La expansión de esto fue bastante peluda (y no voy a hacerlo de memoria), pero usó algunos operadores ternarios para hacer la verificación encuadernada.
No puede hacer esto como una llamada a función en C debido a la necesidad de polimorfismo del objeto devuelto. Por lo tanto, se necesitaba una macro para hacer el tipo de conversión en la expresión. En C ++ puede hacer esto como una llamada a función con sobrecarga de plantilla (probablemente para operador []), pero C no tiene tales características.
Editar: Aquí está el ejemplo del que estaba hablando, del paquete de matriz CAD de Berkeley (edición glu 1.4). La documentación del uso de array_fetch es:
type
array_fetch(type, array, position)
typeof type;
array_t *array;
int position;
Obtener un elemento de una matriz. Se produce un error de tiempo de ejecución en un intento de referencia fuera de los límites de la matriz. No hay verificación de tipo de que el valor en la posición dada sea en realidad del tipo utilizado al desreferenciar la matriz.
y aquí está la macrodefinición de array_fetch (observe el uso del operador ternario y el operador de secuenciación de 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 de array_insert (que hace crecer la matriz si es necesario, como un vector de C ++) es incluso más peluda e involucra múltiples operadores ternarios anidados.
El operador ternario es una conveniencia sintáctica y de legibilidad, no un atajo de rendimiento. Las personas se dividen en los méritos por condicionales de complejidad variable, pero para condiciones breves, puede ser útil tener una expresión de una línea.
Además, dado que es una expresión, como escribió Charlie Martin , eso significa que puede aparecer en el lado derecho de una afirmación en C. Esto es valioso por ser conciso.
El operador ternario puede tener más rendimiento que una cláusula normal si no, esto puede ser crítico en las aplicaciones integradas, pero también la optimización del compilador puede colapsar esta diferencia.
En C, la verdadera utilidad de esto es que es una expresión en lugar de una declaración; es decir, puede tenerlo en el lado derecho (RHS) de una declaración. Entonces puedes escribir ciertas cosas más concisamente.
Es azúcar sintáctica y una abreviatura útil para bloques breves if / else que solo contienen una declaración. Funcionalmente, ambos constructos deberían funcionar de manera idéntica.
Es crucial para la ofuscación de código, así:
Look-> See?!
No
:(
Oh, well
);
Hay muchas cosas en C que no son técnicamente necesarias porque se pueden implementar más o menos fácilmente en términos de otras cosas. Aquí hay una lista incompleta:
- mientras
- para
- funciones
- estructuras
Imagine cómo se vería su código sin estos y puede encontrar su respuesta. El operador ternario es una forma de "azúcar sintáctico" que, si se usa con cuidado y habilidad, facilita la escritura y el entendimiento del código.
Lo mismo que
if(0)
do();
if(0)
{
do();
}
como dwn dijo, la performance fue uno de sus beneficios durante el surgimiento de procesadores complejos, MSDN blog Comportamiento de procesador no clásico: cómo hacer algo puede ser más rápido que no hacerlo, da un ejemplo que indica claramente la diferencia entre el operador ternario (condicional) y declaración if / 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 es muy diferente y extraño (ver el material original). mientras que si cambio:
if (array[i] < boundary) count++;
a
count += (array[i] < boundary) ? 1 : 0;
El tiempo de ejecución ahora es independiente del valor límite, ya que:
el optimizador fue capaz de eliminar la rama de la expresión ternaria.
pero en mi computadora de escritorio intel i5 cpu / windows 10 / vs2015, el resultado de mi prueba es bastante diferente con msdn blog.
cuando se usa el modo de depuración , if / else cost:
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 costo del 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 usa el modo de lanzamiento , si / else costó:
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 costo del 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 la declaración if / else en mi máquina!
de acuerdo con las diferentes técnicas de optimización del compilador, el operador ternal y if / else pueden comportarse de forma muy diferente.
ternario = forma simple de if-else. Está disponible principalmente para legibilidad.
Algunas de las otras respuestas dadas son geniales. Pero estoy sorprendido de que nadie haya mencionado que se puede usar para ayudar a reforzar la precisión de forma compacta.
Algo como esto:
const int n = (x != 0) ? 10 : 20;
así que básicamente n
es un const
cuyo valor inicial depende de una declaración de condición. La alternativa más fácil es hacer n
no una const
, esto permitiría un ordinario if
para inicializarlo. Pero si quieres que sea const
, no se puede hacer con un if
normal. El mejor sustituto que podrías hacer sería usar una función auxiliar como esta:
int f(int x) {
if(x != 0) { return 10; } else { return 20; }
}
const int n = f(x);
pero la versión ternaria si es mucho más compacta y posiblemente más legible.
Algunos de los operadores más oscuros en C existen únicamente porque permiten la implementación de varias macros funcionales como una única expresión que devuelve un resultado. Yo diría que este es el propósito principal por el cual los operadores pueden existir, aunque su funcionalidad sea redundante.
Digamos que deseamos implementar una macro de función que devuelve el mayor de dos parámetros. Entonces sería llamado como por ejemplo:
int x = LARGEST(1,2);
La única forma de implementar esto como una macro de función sería
#define LARGEST(x,y) ((x) > (y) ? (x) : (y))
No sería posible con una instrucción
if ... else
, ya que no devuelve un valor de resultado. Nota)El otro propósito de
?:
Es que en algunos casos realmente aumenta la legibilidad. Más a menudoif...else
es más legible, pero no siempre. Tomemos por ejemplo las sentencias 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 puede ser reemplazado por 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
?:
Nunca da como resultado un código más rápido queif-else
. Ese es un mito extraño creado por principiantes confundidos. En el caso de un código optimizado,?:
Ofrece un rendimiento idéntico aif-else
en la gran mayoría de los casos.En todo caso,
?:
Puede ser más lento queif-else
, ya que viene con promociones obligatorias de tipo implícito, incluso del operando que no se va a usar.?:
Pero?:
Nunca puede ser más rápido queif-else
.
Nota) Ahora, por supuesto, alguien discutirá y se preguntará por qué no utilizar una función. De hecho, si puede usar una función, siempre es preferible a una macro de función. Pero a veces no puedes usar funciones. Supongamos, por ejemplo, que x
en el ejemplo anterior se declara en el alcance del archivo. El inicializador debe ser una expresión constante, por lo que no puede contener una llamada a función. Otros ejemplos prácticos de donde debe usar macros similares a funciones implican un tipo de programación segura con _Generic
o "X macros".