¿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?El operador ternario (condicional) en C
Respuesta
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 Charlie Martin wrote, eso significa que puede aparecer en el lado derecho de una declaración en C. Esto es valioso por ser conciso.
El rendimiento fue uno de sus beneficios durante el surgimiento de los procesadores complejos. No tenía que volcar toda la tubería del procesador para tomar una rama y luego posiblemente realizar una copia adicional, en su lugar, a menudo solo podía insertar un solo valor listo en la tubería. Además, a menudo es más fácil de leer para expresiones de líneas múltiples que algo como 'si (A) return ret1; de lo contrario, si (B) devuelve ret2; ... '. No hay nada difícil de leer en ... return A? ret0: B? ret1: C? ret2: D? ret3; – dwn
El operador ternario también reduce la Complejidad ciclomática del código. –
@AkshayImmanuelD ⇒ Los operadores terciarios no reducen la complejidad ciclomática. El número de rutas a través del código es el mismo ya sea que use un operador ternario o una instrucción if. –
Compacidad y la capacidad de alinear un constructo if-then-else en una expresión.
El aspecto inicial es una diferencia distinta de las otras que creo que han pasado por alto. –
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.
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í es una lista incompleta:
- mientras
- para
- funciones
- estructuras
imaginar 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 usa con cuidado y habilidad, facilita la escritura y el entendimiento del código.
Para continuar con el argumento, realmente no necesitamos C en absoluto porque podemos hacer todo lo necesario con el ensamblador. – Ether
"La portabilidad es para las personas que no pueden escribir nuevos programas". - Linus Torvalds –
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.
Este es EL punto. Convierte un if/else en una expresión, NO una declaración. De alguna manera, sospecho que algunas personas aquí no entienden la diferencia (por favor, abstente de comentar que TÚ lo haces, no te estoy hablando;)). –
@Charlie: +1. Mencioné esto en el mío, pero es bueno hacer de esto un punto explícito. –
Y, debido a esta característica, es una gran herramienta para hacer que el código sea más "funcional" y menos "de procedimiento". –
ternary = forma simple de if-else. Está disponible principalmente para legibilidad.
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.
Esto 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";
Una cosa que me gustaría advertir es atar ternaries 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 & 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 convertirse en:
const int myValue = myCondition ? 42 : 314;
Cuál es mejor es una pregunta debatible que elegiré no debatir.
Estoy de acuerdo con la flagelación, pero me pareció extrañamente legible. :) Seguramente en el ejemplo de prueba con variables alineadas alfabéticamente. –
Sí, se pone realmente desagradable cuando comienzas a poner las cosas entre paréntesis. –
Incluso un solo uso puede provocar errores. Caso en cuestión: su versión de lanzamiento tendrá el título "DEBUG App 1.0". –
Es crucial para la ofuscación de código, así:
Look-> See?!
No
:(
Oh, well
);
Nota: para hacer que el código anterior se compile, simplemente agregue struct {int See;} * Look; int No, Oh, bueno; int main() {/ * código anterior ingresa aquí * /} – Artelius
Algunas de las otras respuestas dadas son grandes. Pero me sorprende que nadie haya mencionado que se puede usar para ayudar a aplicar la corrección const
de forma compacta.
Algo como esto:
const int n = (x != 0) ? 10 : 20;
así que básicamente es un n
const
cuyo valor inicial es dependiente de una instrucción de condición. La alternativa más fácil es hacer que n
no sea const
, esto permitiría que un if
ordinario lo inicialice. Pero si quiere que sea const
, no se puede hacer con un if
ordinario. 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 podría decirse que sea más legible.
Bueno, const * did * vengan aproximadamente, oh, 25 años después del operador condicional. Aunque es un lindo truco. –
Puesto que nadie ha mencionado esto, sin embargo, la única manera de conseguir printf
declaraciones 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 pasa de C a C++ y puede sorprenderse por la (s) falla (s) sutil (s) que surgen de la misma.
El hecho de que el operador ternario sea una expresión, no un enunciado, le permite ser utilizado en macroexpansiones para macros funcionales 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 está 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 de caracteres sin signo para los datos, el tipo era el tipo real de los datos y el índice era 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 se puede hacer esto como una llamada de 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 de función con sobrecarga de plantilla (probablemente para el operador []), pero C no tiene tales características.
Edición: Este es el ejemplo del que estoy hablando, del paquete de matriz CAD de Berkeley (edición glu 1.4). La documentación del uso array_fetch es:
type
array_fetch(type, array, position)
typeof type;
array_t *array;
int position;
Fetch 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 que el valor en la posición dada sea en realidad del tipo utilizado cuando desreferencia 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 de array_insert (que hace crecer la matriz si es necesario, como un vector de C++) es incluso más peluda, involucrando múltiples operadores ternarios anidados.
El mismo que
if(0)
do();
if(0)
{
do();
}
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.
Esta respuesta es una tontería completa. – Lundin
Consulte esta https://www.beningo.com/ternary-operator-versus-the-ifelse-statement/ y encontrará una diferencia en el ensamblaje generado para ARM. El ensamblaje cambiado dará lugar a un rendimiento diferente en diferentes cargas de trabajo y situaciones. –
Guau, ¡qué artículo tan sucio! '(Status.data & 0x10 == 0x10)' es ** un error común de nivel principiante ** debido a la precedencia del operador, donde == tiene una precedencia más alta que &. Este error es tan común que cualquier persona con experiencia real de programación en C incrustada lo detectará instantáneamente. Luego continúa hablando sobre el código de máquina no optimizado de su buggy mess. No tengo idea de quién es "Jacob", pero él es incompetente y no debería escribir blogs. – Lundin
como dwn dijo, el rendimiento fue uno de sus beneficios durante el aumento de los procesadores complejos, MSDN blog Non-classical processor behavior: How doing something can be faster than not doing it da un ejemplo que indica claramente la diferencia entre el operador ternario (condicional) y la instrucció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 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 computadora de escritorio intel i5 cpu/windows 10/vs2015, el resultado de mi prueba es bastante diferente con msdn blog.
cuando se utiliza el modo de depuración, si/costo más:
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
al utilizar el modo de liberación, si/costo más:
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 ter nario costo del operador:
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!
de acuerdo con las diferentes técnicas de optimización del compilador, el operador ternal y if/else pueden comportarse de forma muy diferente.
Algunos de los más oscuros operadores en C existen solamente porque permiten la implementación de diversas macros tipo función como una sola expresión que devuelve un resultado. Diría que este es el objetivo principal por el cual los operadores
?:
y,
pueden existir, incluso si su funcionalidad es redundante.Digamos que deseamos implementar una macro de función que devuelve el mayor de dos parámetros. Sería entonces ser llamado como por ejemplo:
int x = LARGEST(1,2);
La única forma de implementar esto como una macro-función como sería
#define LARGEST(x,y) ((x) > (y) ? (x) : (y))
que no sería posible con una declaració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. Lo más frecuente es queif...else
sea más legible, pero no siempre. Tomemos, por ejemplo, las 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 sustituir por el extremo 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 se
?:
hace resultado en el 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 el mismo rendimiento queif-else
en la gran mayoría de los casos.En todo caso,
?:
puede ser más lenta deif-else
, ya que viene con promociones obligatorias de tipo implícitas, incluso del operando que no va a ser utilizado. Pero?:
nunca puede ser más rápido queif-else
.
Nota) Ahora, por supuesto, alguien va a discutir y se preguntan por qué no utilizar una función. De hecho, si puede usar una función, es siempre preferible sobre 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 una programación segura con _Generic
o "X macros".
- 1. FizzBuzz usando el operador condicional ternario
- 2. ¿Hay un operador ternario condicional en VB.NET?
- 3. python? (condicional/ternario) operador para las asignaciones
- 4. ternario (condicional) del operador y de estilo
- 5. Ventajas del uso del operador condicional?: (Ternario)
- 6. Cómo implementar el operador condicional ternario en MySQL
- 7. tiro y el operador ternario en C++
- 8. Asignar una expresión lambda utilizando el operador condicional (ternario)
- 9. operador Coalesce y el operador condicional en VB.NET
- 10. Ternario? operador contra el operador convencional Si-else en C#
- 11. C++, operador ternario, std :: cout
- 12. Operador condicional C# ¿No es una declaración?
- 13. Asignación dentro de Perl problemas de operador condicional ternario
- 14. ?: Comportamiento del operador condicional ternario al dejar una expresión vacía
- 15. C++ operador condicional
- 16. C#: Operador condicional
- 17. ¿Existe el operador ternario en R?
- 18. Delphi - ¿Equivalente al operador ternario de C#?
- 19. instrucción de retorno en el operador ternario C++
- 20. Cómo utilizar el operador ternario para esta declaración en C#
- 21. ¿El hilo de operador ternario (? :) está seguro en C#?
- 22. Javascript abreviada operador ternario
- 23. Operador condicional en Python?
- 24. ternario Operador similar a:?
- 25. retorno en php operador ternario
- 26. Python operador ternario
- 27. ternario operador en Razor Ver
- 28. ¿Cómo uso el operador condicional?
- 29. instrucción Return utilizando operador ternario
- 30. Usar el operador ternario para operaciones múltiples
Hay muchas otras preguntas sobre el operador ternario para obtener más información sobre su uso. –
Y en realidad se llama el operador condicional. Resulta ser el único operador ternario, pero como Jon Skeet me recordó una vez, siempre podría haber otro más tarde. –
@toast: en realidad 'ternario' es un nombre bastante común para él, si no es más habitual que el condicional – vittore