MIN y MAX en C
c-preprocessor (13)
¿Dónde se definen
MIN
yMAX
en C, en todo caso?
No lo son
¿Cuál es la mejor manera de implementar estos, de la forma más genérica y segura posible del tipo (se prefieren las extensiones / incorporaciones del compilador para compiladores convencionales)?
Como funciones. No usaría macros como #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
, especialmente si planea implementar su código. Escriba su propia cuenta, use algo como fmax
o fmax
estándar, o arregle la macro usando typeof de GCC (usted también obtiene bonificación de seguridad de tipos):
#define max(a,b) /
({ __typeof__ (a) _a = (a); /
__typeof__ (b) _b = (b); /
_a > _b ? _a : _b; })
Todo el mundo dice "oh, yo sé sobre la doble evaluación, no hay problema" y en unos meses, estarás depurando los problemas más tontos durante horas y horas.
Tenga en cuenta el uso de __typeof__
lugar de typeof
:
Si está escribiendo un archivo de encabezado que debe funcionar cuando se incluye en los programas ISO C, escriba
__typeof__
lugar detypeof
.
¿Dónde se definen MIN
y MAX
en C, en todo caso?
¿Cuál es la mejor manera de implementar esto, tan genéricamente y con la mayor seguridad posible? (Se prefieren las extensiones / incorporaciones de compilador para compiladores convencionales).
El máximo de dos enteros a
y b
es (int)(0.5((a+b)+abs(ab)))
. Esto también puede funcionar con (double)
y fabs(ab)
para dobles (similar para flotadores)
Escribí esta version que funciona para MSVC, GCC, C y C ++.
#if defined(__cplusplus) && !defined(__GNUC__)
# include <algorithm>
# define MIN std::min
# define MAX std::max
//# define TMIN(T, a, b) std::min<T>(a, b)
//# define TMAX(T, a, b) std::max<T>(a, b)
#else
# define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) /
({ /
decltype(lexpr) lvar = (lexpr); /
decltype(rexpr) rvar = (rexpr); /
lvar binoper rvar ? lvar : rvar; /
})
# define _CHOOSE_VAR2(prefix, unique) prefix##unique
# define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
# define _CHOOSE(binoper, lexpr, rexpr) /
_CHOOSE2( /
binoper, /
lexpr, _CHOOSE_VAR(_left, __COUNTER__), /
rexpr, _CHOOSE_VAR(_right, __COUNTER__) /
)
# define MIN(a, b) _CHOOSE(<, a, b)
# define MAX(a, b) _CHOOSE(>, a, b)
#endif
Esta es una respuesta tardía, debido a un desarrollo bastante reciente. Desde que el OP aceptó la respuesta que se basa en una extensión no portátil GCC (y clang) typeof
- o __typeof__
para ISO C ''limpio'' - hay una mejor solución disponible a partir de gcc-4.9 .
#define max(x,y) ( /
{ __auto_type __x = (x); __auto_type __y = (y); /
__x > __y ? __x : __y; })
El beneficio obvio de esta extensión es que cada argumento de macro solo se expande una vez, a diferencia de la solución __typeof__
.
__auto_type
es una forma limitada del auto
de C ++ 11. No se puede (o no se debe usar) en el código C ++, aunque no hay una buena razón para no usar las capacidades de inferencia de tipo superior de auto
cuando se usa C ++ 11.
Dicho esto, supongo que no hay problemas con esta sintaxis cuando la macro se incluye en un ámbito extern "C" { ... }
; por ejemplo, desde un encabezado C. AFAIK, esta extensión no ha encontrado su camino.
Evite las extensiones de compilador no estándar e implementarlo como una macro completamente segura para el tipo en el estándar C (ISO 9899: 2011).
Solución
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) /
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
Uso
MAX(int, 2, 3)
Explicación
La macro MAX crea otra macro basada en el parámetro de type
. Esta macro de control, si se implementa para el tipo dado, se usa para verificar que ambos parámetros sean del tipo correcto. Si el type
no es compatible, habrá un error de compilación.
Si x o y no son del tipo correcto, habrá un error de compilación en las macros ENSURE_
. Se pueden agregar más macros de este tipo si se admiten más tipos. He asumido que solo se utilizarán tipos aritméticos (enteros, flotadores, punteros, etc.) y no estructuras o matrices, etc.
Si todos los tipos son correctos, se llamará la macro GENERIC_MAX. Se necesitan paréntesis adicionales alrededor de cada parámetro de macro, como la precaución estándar usual al escribir macros en C.
Luego están los problemas habituales con las promociones de tipo implícitas en C. El operador ?:
Equilibra el segundo y tercer operando entre sí. Por ejemplo, el resultado de GENERIC_MAX(my_char1, my_char2)
sería un int
. Para evitar que la macro realice tales promociones de tipo potencialmente peligrosas, se utilizó un tipo final convertido al tipo deseado.
Razón fundamental
Queremos que ambos parámetros de la macro sean del mismo tipo. Si uno de ellos es de un tipo diferente, la macro ya no es segura, porque un operador como ?:
Producirá promociones de tipo implícitas. Y porque lo hace, también siempre tenemos que devolver el resultado final al tipo deseado, como se explicó anteriormente.
Una macro con un solo parámetro podría haberse escrito de una manera mucho más simple. Pero con 2 o más parámetros, es necesario incluir un parámetro de tipo extra. Porque algo así es desafortunadamente imposible:
// this won''t work
#define MAX(x, y) /
_Generic((x), /
int: GENERIC_MAX(x, ENSURE_int(y)) /
float: GENERIC_MAX(x, ENSURE_float(y)) /
)
El problema es que si la macro anterior se llama como MAX(1, 2)
con dos int
, todavía intentará expandir en macro todos los escenarios posibles de la lista de asociación _Generic
. Por lo tanto, la macro ENSURE_float
también se expandirá, aunque no sea relevante para int
. Y como esa macro solo contiene intencionalmente el tipo float
, el código no se compilará.
Para resolver esto, creé el nombre de la macro durante la fase del preprocesador, con el operador ##, para que ninguna macro se expanda accidentalmente.
Ejemplos
#include <stdio.h>
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) /
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
int main (void)
{
int ia = 1, ib = 2;
float fa = 3.0f, fb = 4.0f;
double da = 5.0, db = 6.0;
printf("%d/n", MAX(int, ia, ib)); // ok
printf("%f/n", MAX(float, fa, fb)); // ok
//printf("%d/n", MAX(int, ia, fa)); compiler error, one of the types is wrong
//printf("%f/n", MAX(float, fa, ib)); compiler error, one of the types is wrong
//printf("%f/n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f/n", MAX(float, da, db)); compiler error, one of the types is wrong
//printf("%d/n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d/n", MAX(int32_t, ia, ib)); // wont get away with this either
return 0;
}
Hay un std::min
y std::max
en C ++, pero AFAIK, no hay equivalente en la biblioteca estándar de C. Puedes definirlas tú mismo con macros como
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
Pero esto causa problemas si escribe algo como MAX(++a, ++b)
.
La forma más sencilla es definirla como una función global en un archivo .h
, y llamarla cuando lo desee, si su programa es modular con muchos archivos. Si no, double MIN(a,b){return (a<b?a:b)}
es la forma más simple.
No creo que sean macros estandarizadas. Ya hay funciones estandarizadas para punto flotante, fmax
y fmax
(y fmaxf
para flotadores, y fmaxl
para dobles largas).
Puede implementarlas como macros siempre que esté al tanto de los problemas de los efectos secundarios / la doble evaluación.
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
En la mayoría de los casos, puede dejarlo en el compilador para determinar lo que está tratando de hacer y optimizarlo lo mejor que pueda. Si bien esto causa problemas cuando se usa como MAX(i++, j++)
, dudo que siempre haya mucha necesidad de verificar el máximo de valores incrementados de una sola vez. Incrementar primero, luego comprobar.
Parece que Windef.h
(a la #include <windows.h>
) tiene macros max
y min
(minúsculas), que también tienen la dificultad de "evaluación doble", pero están ahí para aquellos que no quieren volver a rodar su propia :)
Sé que el tipo dijo "C" ... Pero si tienes la oportunidad, usa una plantilla de C ++:
template<class T> T min(T a, T b) { return a < b ? a : b; }
Escriba seguro, y no hay problemas con el ++ mencionado en otros comentarios.
Si necesita min / max para evitar una rama costosa, no debe usar el operador ternario, ya que se compilará hasta un salto. El siguiente enlace describe un método útil para implementar una función mín / máx sin bifurcar.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
También se proporciona en las versiones GNU libc (Linux) y FreeBSD de sys / param.h, y tiene la definición proporcionada por dreamlax.
En Debian:
$ uname -sr
Linux 2.6.11
$ cat /etc/debian_version
5.0.2
$ egrep ''MIN/(|MAX/('' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.
En FreeBSD:
$ uname -sr
FreeBSD 5.5-STABLE
$ egrep ''MIN/(|MAX/('' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
Los repositorios de origen están aquí:
Vale la pena señalar que creo que si define min
y max
con el terciario como
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
luego, para obtener el mismo resultado para el caso especial de fmin(-0.0,0.0)
y fmax(-0.0,0.0)
necesita intercambiar los argumentos
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)