HOWTO · C

Fonction MIN et MAX en C

On peut utiliser des macros ou des boucles pour implémenter la fonction MIN et MAX en C.

Sur cette page

Pour trouver la plus petite ou la plus grande de deux valeurs en C, définissez une macro MIN ou MAX, ou écrivez une fonction. Le C standard ne fournit pas de fonctions générales intégrées MIN et MAX. Pour un tableau, initialisez le résultat avec son premier élément, puis parcourez les éléments restants.

Les fonctions MIN et MAX sont-elles intégrées au C ?

La bibliothèque standard du C ne définit pas de fonctions générales MIN et MAX. Ces noms sont souvent fournis par un projet sous forme de macros ou de fonctions ordinaires. Une fonction est plus facile à raisonner lorsqu’on lui passe des arguments avec effets de bord ; une macro simple peut être concise si les valeurs sont évaluées sans risque.

La directive #define crée une macro du préprocesseur. Lorsque celui-ci rencontre un appel de macro, il remplace cet appel par le texte de remplacement avant que le compilateur ne traite le programme. À l’inverse, un appel de fonction exécute du code compilé et évalue ses arguments selon les règles d’appel des fonctions en C.

Définir des macros MIN et MAX pour deux valeurs

Pour deux expressions entières sans effets de bord, placez chaque paramètre et l’expression de remplacement complète entre parenthèses. Cela préserve le regroupement attendu si l’appelant transmet une expression telle que a + b.

#include <stdio.h>

#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))

int main(void) {
  printf("Min = %d\n", MIN(0, 10));
  printf("Max = %d\n", MAX(0, 10));
  return 0;
}
Min = 0
Max = 10

L’opérateur conditionnel ?: sélectionne l’expression située après ? si la comparaison est vraie, et celle située après : dans le cas contraire. Les parenthèses gèrent la priorité des opérateurs, mais elles n’empêchent pas les évaluations répétées : dans MIN(a, b), l’argument sélectionné apparaît de nouveau dans le texte de remplacement. Évitez les arguments tels que i++ ou les appels de fonction qui modifient l’état du programme. Cette forme de macro convient surtout aux valeurs simples ; utilisez une fonction si les arguments peuvent avoir des effets de bord.

Utiliser une macro GNU C pour évaluer les arguments une seule fois

GNU C prend en charge les expressions contenant des instructions (statement expressions), ainsi que typeof (écrit __typeof__ dans cet exemple). La macro ci-dessous stocke chaque argument dans une variable temporaire, puis compare ces valeurs. Chaque expression argument est évaluée une seule fois lors de son initialisation. Il s’agit d’une extension de GNU C : elle n’est donc pas portable en ISO C et peut nécessiter une option du compilateur qui active les extensions GNU.

#include <stdio.h>

#define MAX(x, y)                    \
  ({                                 \
    __typeof__(x) _max_x = (x);      \
    __typeof__(y) _max_y = (y);      \
    _max_x > _max_y ? _max_x : _max_y; \
  })

#define MIN(x, y)                    \
  ({                                 \
    __typeof__(x) _min_x = (x);      \
    __typeof__(y) _min_y = (y);      \
    _min_x < _min_y ? _min_x : _min_y; \
  })

int main(void) {
  int a = 4;
  int b = 9;
  printf("Min = %d\n", MIN(a++, b));
  printf("Max = %d\n", MAX(a, b));
  return 0;
}
Min = 4
Max = 9

Ce schéma convient aux expressions scalaires ordinaires dont les types peuvent être comparés avec < ou >. Ce n’est pas une fonction générique adaptée à tous les types : des types incompatibles, des pointeurs sans ordre pertinent et des types qui ne prennent pas en charge ces opérateurs nécessitent une autre conception. L’expression conditionnelle suit également les règles habituelles de conversion des types en C. Pour écrire du code portable, préférez une fonction typée ou écrivez des fonctions distinctes pour les types pris en charge.

Trouver les valeurs minimale et maximale dans un tableau

Pour un tableau, conservez les valeurs minimale et maximale courantes et mettez-les à jour au fil du parcours. Initialisez-les toutes les deux avec le premier élément afin que le résultat corresponde à une valeur réellement présente dans le tableau. L’exemple reprend les mêmes valeurs que l’exemple de tableau d’origine et parcourt celui-ci une seule fois.

#include <stdio.h>
#include <stddef.h>

void find_min_max(const int values[], size_t length, int *minimum, int *maximum) {
  *minimum = values[0];
  *maximum = values[0];

  for (size_t i = 1; i < length; ++i) {
    if (values[i] < *minimum) {
      *minimum = values[i];
    }
    if (values[i] > *maximum) {
      *maximum = values[i];
    }
  }
}

int main(void) {
  int values[] = {6, 3, 15, 9, 10, 50, 99, 11};
  size_t length = sizeof(values) / sizeof(values[0]);
  int minimum;
  int maximum;

  find_min_max(values, length, &minimum, &maximum);
  printf("Max number = %d\n", maximum);
  printf("Min number = %d\n", minimum);
  return 0;
}
Max number = 99
Min number = 3

Cette fonction nécessite un tableau non vide et des pointeurs de résultat valides. L’appelant transmet la longueur, car un paramètre déclaré comme tableau est ajusté en pointeur ; dans find_min_max, sizeof(values) mesure donc le pointeur, et non le tableau d’origine. Ici, sizeof permet de calculer le nombre d’éléments dans main, où values est encore un tableau : divisez sa taille totale par celle d’un élément.

Pour des données à virgule flottante, utilisez un type cohérent pour le tableau, les variables minimale et maximale ainsi que les paramètres de la fonction. Faites correspondre la conversion de printf au type de l’argument après promotion ; pour un double, utilisez %f. Si la fonction doit accepter plusieurs types numériques, écrivez des fonctions adaptées à chaque type ou concevez une abstraction avec soin, plutôt que de remplacer simplement int par float.