MIN et MAX en C
où MIN
et MAX
sont-ils définis en C, le cas échéant?
Quelle est la meilleure façon de les mettre en œuvre, aussi généreusement et dactylographier en toute sécurité que possible? (Extensions/builtins de compilateurs pour les compilateurs traditionnels préférés.)
13 réponses
où
MIN
etMAX
sont-ils définis en C, le cas échéant?
ils ne le sont pas.
Quelle est la meilleure façon de les mettre en œuvre, de manière aussi générale et aussi sûre que possible (extensions de compilateurs/builtins pour les compilateurs traditionnels préférés).
comme fonctions. Je n'utiliserais pas de macros comme #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
, surtout si vous prévoyez de déployer votre code. Soit écrire la vôtre, utilisez quelque chose comme la norme fmax
ou fmin
, ou fixez la macro en utilisant type de GCC (vous obtenez aussi un bonus de sécurité):
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
tout le monde dit "oh je sais À propos de la double évaluation, ce n'est pas un problème" et quelques mois plus tard, vous débuggerez les problèmes les plus stupides pendant des heures.
noter l'utilisation de __typeof__
au lieu de typeof
:
Si vous écrivez un fichier d'en-tête doit fonctionner lorsqu'il est inclus dans L'ISO C programmes, écrire
__typeof__
au lieu detypeof
.
est également fourni dans les versions GNU libc (Linux) et FreeBSD de sys/param.h, et a la définition fournie par dreamlax.
Sur 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.
Sous 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))
les dépôts sources sont ici:
il y a un std::min
et std::max
en C++, mais AFAIK, il n'y a pas d'équivalent dans la bibliothèque standard C. Vous pouvez les définir vous-même avec des macros comme
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
mais cela cause des problèmes si vous écrivez quelque chose comme MAX(++a, ++b)
.
éviter les extensions de compilateurs non standard et les mettre en œuvre comme une macro complètement sans danger dans la norme C pure (ISO 9899: 2011).
Solution
#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))
Utilisation
MAX(int, 2, 3)
explication
la macro MAX crée une autre macro basée sur le paramètre type
. Cette macro de contrôle, si elle est implémentée pour le type donné, est utilisée pour vérifier que les deux paramètres sont du type correct. Si le type
n'est pas supporté, il y aura une erreur de compilation.
Si x ou y n'est pas du type correct, il y aura une erreur de compilation dans les macros ENSURE_
. Plus de telles macros peuvent être ajoutées si plus de types sont pris en charge. J'ai supposé que seuls les types arithmétiques (entiers, flotteurs, pointeurs, etc.) seront utilisés et non les structures ou tableaux, etc.
si tous les types sont corrects, la macro GENERIC_MAX sera appelée. Des parenthèses supplémentaires sont nécessaires autour de chaque paramètre macro, comme la précaution standard habituelle lors de l'écriture des macros C.
puis il y a les problèmes habituels avec les promotions de type implicites en C. l'opérateur ?:
balance le 2e et le 3e opérande l'un contre l'autre. Par exemple, le résultat de GENERIC_MAX(my_char1, my_char2)
serait une int
. Pour empêcher la macro de faire promotions de type potentiellement dangereux, un moulage de type final au type prévu a été utilisé.
"1519200920 la" Justification
nous voulons que les deux paramètres de la macro soient du même type. Si l'un d'entre eux est d'un type différent, la macro n'est plus sécurisé, car un opérateur comme ?:
rendement implicite du type de promotions. Et parce qu'il le fait, nous avons aussi toujours besoin de mouler le résultat final de nouveau au type prévu comme expliqué surtout.
une macro avec un seul paramètre aurait pu être écrite d'une manière beaucoup plus simple. Mais avec 2 paramètres ou plus, il est nécessaire d'inclure un paramètre de type supplémentaire. Parce que quelque chose comme cela est malheureusement impossible:
// this won't work
#define MAX(x, y) \
_Generic((x), \
int: GENERIC_MAX(x, ENSURE_int(y)) \
float: GENERIC_MAX(x, ENSURE_float(y)) \
)
le problème est que si la macro ci-dessus est appelée MAX(1, 2)
avec deux int
, il va encore essayer de macro-étendre tous les scénarios possibles de la liste d'association _Generic
. De sorte que le ENSURE_float
macro sera élargi aussi, même si elle n'est pas pertinente pour int
. Et comme cette macro ne contient intentionnellement que le type float
, le code ne sera pas compilé.
pour résoudre cela, j'ai créé le nom de la macro pendant la phase de pré-processeur à la place, avec l'opérateur##, de sorte qu'aucune macro ne soit accidentellement étendue.
exemples
#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;
}
Je ne pense pas qu'il s'agisse de macros standardisées. Il existe déjà des fonctions normalisées pour les flotteurs, fmax
et fmin
(et fmaxf
pour les flotteurs, et fmaxl
pour les doubles longs).
vous pouvez les mettre en œuvre comme macros aussi longtemps que vous êtes au courant des questions des effets secondaires/double-évaluation.
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
dans la plupart des cas, vous pouvez laisser au compilateur le soin de déterminer ce que vous essayez de faire et d'optimiser elle du mieux qu'elle peut. Bien que cela cause des problèmes quand utilisé comme MAX(i++, j++)
, je doute qu'il y ait jamais beaucoup besoin de vérifier le maximum de valeurs incrémentées en une seule fois. Incrément d'abord, puis vérifier.
C'est une réponse tardive, en raison d'une assez récente. Puisque L'OP a accepté la réponse qui repose sur une extension GCC (et clang) non portable typeof
-ou __typeof__
pour "propre" ISO C - Il ya une meilleure solution disponible à partir de gcc - 4.9 .
#define max(x,y) ( \
{ __auto_type __x = (x); __auto_type __y = (y); \
__x > __y ? __x : __y; })
l'avantage évident de cette extension est que chaque argument macro n'est développé qu'une seule fois, contrairement à la solution __typeof__
.
__auto_type
est une forme limitée de C++11 auto
. Il ne peut pas (ou ne devrait pas?) soit utilisé dans le code C++, bien qu'il n'y ait aucune bonne raison de ne pas utiliser les capacités d'inférence de type supérieur de auto
en utilisant C++11.
cela dit, je assumer il n'y a aucun problèmes à l'aide de cette syntaxe lorsque la macro est inclus dans un extern "C" { ... }
champ d'application; par exemple, à partir d'un C-tête. AFAIK, cette extension n'a pas trouvé sa voie info clang
j'ai écrit cette version qui fonctionne pour MSVC, GCC, C, et 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
si vous avez besoin de min/max pour éviter une branche coûteuse, vous ne devriez pas utiliser l'opérateur ternaire, car il va compiler jusqu'à un saut. Le lien ci-dessous décrit une méthode utile pour implémenter une fonction min/max sans branchement.
http://graphics.stanford.edu seander bithacks.html#IntegerMinOrMax
je sais que le type a dit "C"... Mais si vous en avez la chance, utilisez un modèle C++:
template<class T> T min(T a, T b) { return a < b ? a : b; }
Tapez safe, et aucun problème avec le ++ mentionné dans d'autres commentaires.
il est intéressant de noter que je pense que si vous définissez min
et max
avec le tertiaire tel que
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
, puis pour obtenir le même résultat pour le cas particulier de fmin(-0.0,0.0)
et fmax(-0.0,0.0)
vous devez échanger les arguments
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
ressemble à Windef.h
(un la #include <windows.h>
) a max
et min
(minuscule) macros, qui souffrent également de la difficulté de "double évaluation", mais ils sont là pour ceux qui ne veulent pas re-roll leur propre :)
le maximum de deux entiers a
et b
est (int)(0.5((a+b)+abs(a-b)))
. Cela peut aussi fonctionner avec (double)
et fabs(a-b)
pour les doubles (similaire pour les flotteurs)
la façon la plus simple est de le Définir comme une fonction globale dans un fichier .h
, et de l'appeler quand vous voulez, si votre programme est modulaire avec beaucoup de fichiers. Si non, double MIN(a,b){return (a<b?a:b)}
est la manière la plus simple.