MIN и MAX в C

Где MIN и MAX, определенные в C, если вообще?

Каков наилучший способ их реализации, как можно более универсально и безопасно? (Предпочтительнее использовать расширения/встроенные компиляторы для компиляторов основного потока.)

Ответ 1

Где MIN и MAX, определенные в C, если вообще?

Это не так.

Каков наилучший способ их реализации, как можно более универсальный и безопасный тип (предпочтительны расширения/встроенные компиляторы для компиляторов основного потока).

Как функции. Я бы не использовал макросы типа #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), особенно если вы планируете развернуть свой код. Либо напишите свой собственный, используйте что-то вроде стандартного fmax или fmin, или исправить макрос, используя GCC typeof (вы также получаете бонусы типов):

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

Все говорят: "О, я знаю о двойной оценке, это не проблема" и несколько месяцев спустя, вы будете отлаживать самые глупые проблемы в течение нескольких часов подряд.

Обратите внимание на использование __typeof__ вместо typeof:

Если вы пишете заголовочный файл, который должен работать, когда включен в ISO C программ, напишите __typeof__ вместо typeof.

Ответ 2

Он также представлен в версиях sys/param.h GNU libc (Linux) и FreeBSD и имеет определение, предоставленное dreamlax.


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

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

Исходные репозитории находятся здесь:

Ответ 3

Там есть std::min и std::max в С++, но AFAIK, нет эквивалента в стандартной библиотеке C. Вы можете определить их самостоятельно с помощью макросов, например

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

Но это вызывает проблемы, если вы пишете что-то вроде MAX(++a, ++b).

Ответ 4

Я не думаю, что они стандартизированные макросы. Существуют стандартизированные функции для с плавающей запятой уже, fmax и fminfmaxf для поплавков и fmaxl для длинных удвоений).

Вы можете реализовать их как макросы, если вам известны проблемы с побочными эффектами/двойной оценкой.

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

В большинстве случаев вы можете оставить его компилятору, чтобы определить, что вы пытаетесь сделать, и оптимизировать его, насколько это возможно. Хотя это вызывает проблемы при использовании, например, MAX(i++, j++), я сомневаюсь, что когда-либо возникает необходимость в проверке максимального количества добавленных значений за один раз. Сначала добавьте, затем проверьте.

Ответ 5

Это поздний ответ из-за довольно недавнего развития. Поскольку OP принял ответ, который полагается на не переносимое расширение GCC (и clang) typeof - или __typeof__ для "чистого" ISO C - там есть лучшее решение, доступное как GCC-4.9.

#define max(x,y) ( \
    { __auto_type __x = (x); __auto_type __y = (y); \
      __x > __y ? __x : __y; })

Очевидным преимуществом этого расширения является то, что каждый аргумент макроса расширяется только один раз, в отличие от решения __typeof__.

__auto_type является ограниченной формой С++ 11 auto. Он не может (или не должен?) Использоваться в коде С++, хотя нет веских оснований не использовать возможности вывода превосходного типа auto при использовании С++ 11.

Тем не менее, я полагаю, что нет проблем с использованием этого синтаксиса, когда макрос включен в область extern "C" { ... }; например, из заголовка С. AFAIK, это расширение не нашло своего пути info clang

Ответ 6

Избегайте нестандартных расширений компилятора и реализуйте его как полностью безопасный тип макроса в чистом стандарте C (ISO 9899: 2011).

Решение

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

Использование

MAX(int, 2, 3)

Объяснение

Макрос MAX создает другой макрос, основанный на параметре type. Этот управляющий макрос, если он реализован для данного типа, используется для проверки того, что оба параметра имеют правильный тип. Если type не поддерживается, будет ошибка компилятора.

Если x или y не имеют правильный тип, в макросах ENSURE_ будет ошибка компилятора. Больше таких макросов можно добавить, если поддерживаются больше типов. Я предположил, что будут использоваться только арифметические типы (целые числа, поплавки, указатели и т.д.), А не структуры или массивы и т.д.

Если все типы верны, будет вызываться макрос GENERIC_MAX. Дополнительная дополнительная скобка необходима вокруг каждого макропараметра, как обычная стандартная мера предосторожности при написании макросов C.

Тогда возникают обычные проблемы с неявными типами продвижения в C. Оператор ?: уравновешивает 2-й и 3-й операнды друг против друга. Например, результатом GENERIC_MAX(my_char1, my_char2) будет int. Чтобы предотвратить макрос в выполнении таких потенциально опасных рекламных акций, был использован последний тип, который был выбран для предполагаемого типа.

Обоснование

Мы хотим, чтобы оба параметра для макроса были одного типа. Если один из них имеет другой тип, макрос больше не является безопасным для типа, потому что оператор типа ?: даст неявные рекламные кампании типа. И поскольку это так, нам также всегда нужно вернуть окончательный результат к предполагаемому типу, как описано выше.

Макрос с одним параметром мог быть написан гораздо проще. Но с двумя или более параметрами необходимо включить дополнительный параметр типа. Потому что что-то вроде этого, к сожалению, невозможно:

// this won't work
#define MAX(x, y)                                  \
  _Generic((x),                                    \
           int: GENERIC_MAX(x, ENSURE_int(y))      \
           float: GENERIC_MAX(x, ENSURE_float(y))  \
          )

Проблема заключается в том, что если указанный макрос называется MAX(1, 2) с двумя int, он все равно попытается выполнить макрораспределение всех возможных сценариев списка ассоциаций _Generic. Таким образом, макрос ENSURE_float также будет расширен, хотя это не относится к int. И поскольку этот макрос намеренно содержит только тип float, код не будет компилироваться.

Чтобы решить эту проблему, я создал имя макроса на фазе предварительного процессора, вместо этого, с помощью оператора ##, так что макрос случайно не будет расширен.

<сильные > Примеры

#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;
}

Ответ 7

Я написал эту версию, которая работает для MSVC, GCC, 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

Ответ 8

Если вам нужно min/max, чтобы избежать дорогостоящей ветки, вы не должны использовать тернарный оператор, так как он скомпилируется до перехода. Ниже приведена ссылка на полезный метод реализации функции min/max без ветвления.

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

Ответ 9

Я знаю, что парень сказал "C"... Но если у вас есть такая возможность, используйте шаблон С++:

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

Введите безопасный код и никаких проблем c++, упомянутых в других комментариях.

Ответ 10

Стоит отметить, что, если вы определяете min и max с третичным, например

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

то для получения того же результата для частного случая fmin(-0.0,0.0) и fmax(-0.0,0.0) вам необходимо поменять параметры

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

Ответ 11

Похож, что Windef.h (a la #include <windows.h>) имеет макросы max и min (нижний регистр), которые также страдают от сложности с двойной оценкой, но они есть для тех, t хотите перевернуть свои собственные:)

Ответ 12

Максимум двух целых чисел a и b равен (int)(0.5((a+b)+abs(a-b))). Это может также работать с (double) и fabs(a-b) для двойников (аналогично для float)