Recientemente escribí un programa que ordena una matriz. Para ello, necesitaba escribir una función de comparación, que pasaré a ella. Mi función de comparación debería haber devuelto 1 (si x> y), -1 (si x <y) o 0 (si x = y). Escribí una función regular (Función 1) usando expresiones condicionales, pero me aconsejaron que escribiera de manera diferente (Función 2). ¿Es mejor escribir así? ¿Una condición booleana siempre devolverá 1 para la verdad? (Quiero decir, si x = 0 e y = 0 siempre tendremos (x == y) == 1?)
Función 1:
int Icmp(void* x, void* y)
{
int a = *(int*)x;
int b = *(int*)y;
if (a > b)
return 1;
else if (a < b)
return -1;
else
return 0;
}
Función 2:
int Icmp(void* x, void* y)
{
return (*(int*)x > * (int*)y) - (*(int*)x < *(int*)y);
}
Respuestas:
La forma preferida de escribir el código no ramificado sería utilizar una variable local para los operandos:
La expresión es un idioma común en las funciones de comparación, y si se escribe usando variables en lugar de desreferencias de puntero en el lugar, también es bastante legible.
El código se basa en el hecho de que el resultado de una comparación usando
>
,<
o incluso==
es de tipoint
1 y 0. Esto es requerido por el estándar C: cualquier compilador que genere valores como 42 o -1 no es, por definición, un compilador C .Es fácil ver que max. uno de
a > b
oa < b
puede ser cierto en un momento dado, y el resultado es1 - 0
,0 - 1
o0 - 0
.En cuanto a por qué el código sin ramificación, si bien los compiladores pueden generar exactamente el mismo código para ambas funciones, a menudo no lo hacen. Por ejemplo, los últimos GCC e ICC parecen generar una ramificación para la primera función en x86-64, pero un código sin ramificación con ejecución condicional para la última. Y a cualquiera que diga que las ramas no importan, entonces lo remito al control de calidad más votado de la historia en Stack Overflow .
fuente
if
declaración no significa que no se generará ninguna rama, ni que si hay unaif
declaración de que habrá una rama. De hecho, depende de si el compilador puede optimizarlos.Yo diría que no.
Para el rendimiento; o no importa (probablemente para los compiladores modernos), o no debería ser una función separada (y debería estar integrada en el código utilizado para ordenar), o no debería estar ordenando en absoluto (por ejemplo, datos ordenados en la creación y no ordenado después de la creación).
Para facilitar la lectura (mantenimiento del código, posibilidad de ver errores en la versión original, riesgo de introducir errores más tarde) Prefiero su versión original; especialmente cuando se trabaja en un equipo, y especialmente cuando otros miembros del equipo están más familiarizados con otros 10 lenguajes de programación que tienen reglas muy diferentes a las de C.
Específicamente; Me gusta esto (porque los lanzamientos en el código real hacen que las cosas sean más difíciles de leer):
..y reescribiría el resto para que se vea así:
..o para verse así:
..porque
else
es innecesario después de unreturn
; y porque "sin llaves seguidas de una declaración en su propia línea" crea el riesgo de que alguien inserte accidentalmente una nueva línea sin darse cuenta y rompa todo (por ejemplo, consulte https://dwheeler.com/essays/apple-goto- fail.html ).fuente
Si está utilizando la función de comparación con
qsort
, la función solo necesita devolver valores + ve, -ve o cero.En ese caso, solo puedes restar los números
fuente
(a > b) - (a < b)
, pero el problemaa - b
es que la resta puede desbordarse. Por lo tanto, esta es una mala expresión.Enteros
Carrozas
Instrumentos de cuerda
fuente