Question

Où sont MIN et MAX définis dans C, le cas échéant?

Quelle est la meilleure façon de les mettre en œuvre, comme génériquement et le type que possible en toute sécurité? (Extensions du compilateur / builtins pour les compilateurs traditionnels préférés.)

Était-ce utile?

La solution

  

Où sont MIN et MAX définis dans C, le cas échéant?

Ils ne sont pas.

  

Quelle est la meilleure façon de les mettre en œuvre, comme génériquement et le type sûr que possible (extensions du compilateur / builtins pour les compilateurs traditionnels préférés).

fonctions. Je ne voudrais pas utiliser des macros comme #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), surtout si vous envisagez de déployer votre code. Soit écrire votre propre, utilisez quelque chose comme norme fmax ou fmin ou fixer la macro à l'aide GCC typeof (vous obtenez un bonus de trop Sûreté du typage):

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

Tout le monde dit « oh je sais à double évaluation, il n'y a pas de problème » et quelques mois sur la route, vous serez débogage les plus sottes problèmes pendant des heures.

Notez l'utilisation de __typeof__ au lieu de typeof:

  

Si vous écrivez un fichier d'en-tête   doit travailler lorsqu'il est inclus dans la norme ISO C   programmes, __typeof__ d'écriture au lieu de   typeof.

Autres conseils

Il est également prévu dans la GNU libc (Linux) et les versions de FreeBSD de sys / param.h, et a la définition donnée 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.

Sur 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 référentiels sources sont ici:

Évitez les extensions du compilateur non standard et mettre en œuvre comme une macro complètement de type sécurisé 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 en fonction du paramètre de type. Cette macro de contrôle, si elles sont appliquées pour le type donné, est utilisé pour vérifier que les deux paramètres sont du type correct. Si le type est pas pris en charge, il y aura une erreur de compilation.

Si x ou y est pas du type correct, il y aura une erreur de compilation dans les macros ENSURE_. Plus ces macros peuvent être ajoutées si plusieurs types sont pris en charge. Je suis supposé que seuls les types arithmétiques (entiers, flottants, pointeurs, etc.) seront utilisés et non struct ou des tableaux, etc.

Si tous les types sont corrects, la macro GENERIC_MAX sera appelée. parenthèses supplémentaires sont nécessaires autour de chaque paramètre macro, comme la précaution standard habituel lors de l'écriture des macros C.

Ensuite, il y a les problèmes habituels avec les promotions de type implicites dans C. Le ?:operator équilibre entre le 2ème et 3ème opérande contre l'autre. Par exemple, le résultat de GENERIC_MAX(my_char1, my_char2) serait un int. Pour éviter que la macro de faire de telles promotions de type potentiellement dangereux, on a utilisé un dernier type coulée du type prévu.

Justification

Nous voulons que les deux paramètres à la macro à être du même type. Si l'un d'entre eux est d'un type différent, la macro est plus en sécurité de type, car un opérateur comme ?: donnera des promotions de type implicite. Et parce qu'il fait, il nous faut aussi toujours jeter le dos de résultat final du type conçu comme expliqué ci-dessus.

Une macro avec un seul paramètre aurait pu être écrit d'une manière beaucoup plus simple. Mais, il y a 2 ou plusieurs paramètres 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é comme MAX(1, 2) avec deux int, il sera toujours essayer de macro-développer tous les scénarios possibles de la liste des associations de _Generic. Ainsi, la macro ENSURE_float va s'élargi aussi, même si elle ne concerne pas les int. Et puisque cette macro contient intentionnellement que le type de float, le code ne compilera pas.

Pour résoudre ce problème, je crée le nom de la macro au cours de la phase de pré-processeur à la place, avec l'opérateur ##, de sorte qu'aucune macro est accidentellement étendu.

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'ils sont des macros normalisées. Il existe des fonctions standardisées pour déjà virgule flottante, fmax et fmin (et fmaxf pour les flotteurs et fmaxl en double longueur).

Vous pouvez les mettre en œuvre des macros aussi longtemps que vous êtes au courant des problèmes de 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 le laisser au compilateur de déterminer ce que vous essayez de faire et optimiser le mieux possible. Bien que cela pose des problèmes quand il est utilisé comme MAX(i++, j++), je doute qu'il y ait jamais besoin de beaucoup à vérifier le maximum des valeurs incrémentée en une seule fois. Incrémenter d'abord, puis vérifier.

Ceci est une réponse tardive, en raison d'un développement assez récent. Depuis l'OP a accepté la réponse qui repose sur un GCC non portable (et clang) l'extension typeof - ou __typeof__ pour 'clean' ISO C - il y a 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 est une seule fois étendu, contrairement à la solution __typeof__.

__auto_type est une forme limitée de auto de C ++ 11. Il ne peut pas (ou ne devrait pas?) Être utilisé dans le code de C, mais il n'y a aucune bonne raison de ne pas utiliser les capacités d'inférence de type supérieur de auto lors de l'utilisation C ++ 11.

Cela dit, je supposons il n'y a pas de problèmes en utilisant cette syntaxe lorsque la macro est inclus dans un périmètre de extern "C" { ... }; par exemple, à partir d'un en-tête C. Autant que je sache, cette extension n'a pas trouvé son info façon clang

J'ai écrit ce 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 min / max pour éviter une branche coûteuse, vous ne devriez pas utiliser l'opérateur ternaire, comme il compilera jusqu'à un saut. Le lien ci-dessous décrit un procédé utile pour la mise en oeuvre d'une fonction de min / max sans ramification.

http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax

Il est intéressant de souligner Je pense que si vous définissez min et max avec le tertiaire tels 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 avez besoin d'échanger les arguments

fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)

On dirait Windef.h (à la #include <windows.h>) a max et min macros (minuscules), qui souffrent également de la difficulté « double évaluation », mais ils sont là pour ceux qui ne veulent pas re-roll leur propre :)

Je sais que le gars a dit « C » ... Mais si vous avez la chance, utilisez un template C ++:

template<class T> T min(T a, T b) { return a < b ? a : b; }

sécurité de type et aucun problème avec le ++ mentionné dans d'autres commentaires.

Le maximum de deux nombres entiers a et b est (int)(0.5((a+b)+abs(a-b))). Cela peut aussi travailler avec (double) et fabs(a-b) en double (similaire pour les flotteurs)

commentaire Brett Hale , clang a commencé à soutenir __auto_type autour de 2016 (voir Patch ).

La façon la plus simple est de le définir comme une fonction globale dans un fichier .h, et l'appeler quand vous voulez, si votre programme est modulaire avec beaucoup de fichiers. Dans le cas contraire, double MIN(a,b){return (a<b?a:b)} est le moyen le plus simple.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top