MIN et MAX en C

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.)

224
demandé sur a3f 2010-08-09 08:49:40

13 réponses

MIN et MAX 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 de typeof .

298
répondu David Titarenco 2014-11-02 18:07:04

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:

74
répondu Mikel 2010-08-15 02:56:13

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) .

63
répondu dan04 2010-08-09 04:56:57

é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;
}
17
répondu Lundin 2015-06-18 14:29:59

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.

16
répondu dreamlax 2010-08-09 04:58:50

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

14
répondu Brett Hale 2015-08-20 00:18:05

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
11
répondu Matt Joiner 2012-11-09 21:39:19

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

8
répondu cib 2011-09-19 18:13:05

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.

5
répondu Bas Kuenen 2013-03-14 11:23:36

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)
3
répondu Z boson 2015-06-18 12:42:57

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 :)

2
répondu rogerdpack 2016-12-05 03:51:22

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)

0
répondu NRZ 2014-07-21 10:14:35

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.

-3
répondu srezat 2018-04-12 21:29:47