Где MIN
и MAX
определены в C, если вообще?
Каков наилучший способ реализовать их как можно более обобщенно и безопасно? (Расширения / встроенные компиляторы для основных компиляторов предпочтительнее.)
Где MIN
и MAX
определены в C, если вообще?
Каков наилучший способ реализовать их как можно более обобщенно и безопасно? (Расширения / встроенные компиляторы для основных компиляторов предпочтительнее.)
Ответы:
Где
MIN
иMAX
определены в C, если вообще?
Это не так.
Каков наилучший способ их реализации, максимально общий и безопасный для типов (предпочтительны расширения / встроенные компиляторы для основных компиляторов).
Как функции. Я бы не стал использовать такие макросы #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
, особенно если вы планируете развернуть свой код. Либо написать свое собственное, использовать что - то вроде стандарта fmax
или fmin
, или исправить макрос с помощью TYPEOF GCC в (вы получите бонус типобезопасности тоже) в выражении НКИ заявления :
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
Все говорят: «О, я знаю о двойной оценке, это не проблема», и через несколько месяцев вы будете часами подряд отлаживать самые глупые проблемы.
Обратите внимание на использование __typeof__
вместо typeof
:
Если вы пишете заголовочный файл, который должен работать при включении в программы ISO C, пишите
__typeof__
вместоtypeof
.
decltype
ключевым словом MSVC ++ 2010 - но даже в этом случае Visual Studio не может выполнять составные операторы в макросах. (и decltype
в любом случае это C ++), то есть ({ ... })
синтаксис GCC, так что я уверен, что в любом случае это невозможно. Я не смотрел на другие компиляторы по этой проблеме, извините Лютер: S
MAX(someUpperBound, someRandomFunction())
ограничить случайное значение до некоторой верхней границы. Это была ужасная идея, но она также не работала, потому что MAX
он использовал проблему двойной оценки, поэтому он получил случайное число, отличное от того, которое было первоначально оценено.
MIN(x++, y++)
препроцессор, будет сгенерирован следующий код (((x++) < (y++)) ? (x++) : (y++))
. Итак, x
и y
будет увеличен в два раза.
Он также предоставляется в версиях 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))
Исходные репозитории находятся здесь:
openSUSE/Linux 3.1.0-1.2-desktop
/ gcc version 4.6.2 (SUSE Linux)
тоже. :) Плохо, что это не портативно.
Есть std::min
и std::max
в C ++, но AFAIK, в стандартной библиотеке C нет эквивалента. Вы можете определить их самостоятельно с помощью макросов, таких как
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
Но это вызывает проблемы, если вы пишете что-то вроде MAX(++a, ++b)
.
#define MIN(A, B) ((A < B) ? A : B)
это не гибкий способ, почему ???
#define MULT(x, y) x * y
. Затем MULT(a + b, a + b)
расширяется до a + b * a + b
, который анализирует как a + (b * a) + b
из-за приоритета. Это не то, что программист, вероятно, намеревался.
Избегайте нестандартных расширений компилятора и реализуйте его как полностью безопасный для типов макрос в чистом стандарте 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. ?:
Оператор уравновешивает 2-й и 3-й операнд друг против друга. Например, результатом GENERIC_MAX(my_char1, my_char2)
будет int
. Чтобы макрос не мог выполнять такие потенциально опасные продвижения типа, использовался финальный тип, приведенный к предполагаемому типу.
обоснование
Мы хотим, чтобы оба параметра макроса были одного типа. Если один из них имеет другой тип, макрос больше не является типобезопасным, потому что оператор like ?:
будет выдавать неявные продвижения типа. И поскольку это так, мы также всегда должны приводить конечный результат обратно к намеченному типу, как описано выше.
Макрос только с одним параметром мог бы быть написан намного проще. Но с 2 или более параметрами необходимо включить дополнительный параметр типа. Потому что что-то подобное, к сожалению, невозможно:
// 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;
}
GENERIC_MAX
Кстати, этот макрос - плохая идея, вам нужно только попытаться GENERIC_MAX(var++, 7)
выяснить, почему :-) В настоящее время (особенно с сильно оптимизирующими / встроенными компиляторами), макросы в значительной степени следует относить только к простым формам. Подобные функции лучше как функции, а группы значений лучше как перечисления.
Я не думаю, что это стандартизированные макросы. Уже есть стандартизированные функции для чисел с плавающей точкой fmax
и fmin
(и fmaxf
для чисел с плавающей запятой, и fmaxl
для длинных двойных чисел ).
Вы можете реализовать их как макросы, если вы знаете о побочных эффектах / двойной оценке.
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
В большинстве случаев вы можете оставить это на усмотрение компилятора, чтобы определить, что вы пытаетесь сделать, и оптимизировать его как можно лучше. Хотя это вызывает проблемы при использовании как MAX(i++, j++)
, я сомневаюсь, что когда-либо есть необходимость в проверке максимума приращенных значений за один раз. Сначала увеличьте, затем проверьте.
Это поздний ответ из-за довольно недавнего развития событий. Поскольку 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
это ограниченная форма C ++ 11 auto
. Он не может (или не должен?) Использоваться в коде C ++, хотя нет веской причины не использовать возможности превосходного вывода типов auto
при использовании C ++ 11.
Тем не менее, я предполагаю, что нет никаких проблем с использованием этого синтаксиса, когда макрос включен в extern "C" { ... }
область действия; например, из заголовка C. AFAIK, это расширение не нашло свой путь
clang
начал поддерживать __auto_type
около 2016 года (см. Патч ).
c-preprocessor
тег. Функция не гарантируется встроенной даже с указанным ключевым словом, если только не используется что-то вроде __always_inline__
атрибута gcc .
Я написал эту версию, которая работает для MSVC, GCC, C и 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
Если вам нужно мин / макс, чтобы избежать дорогостоящей ветки, вам не следует использовать троичный оператор, так как он скомпилируется до прыжка. Ссылка ниже описывает полезный метод для реализации функции min / max без ветвления.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
@David Titarenco прибил его здесь , но позвольте мне , по крайней мере в чистоте его немного , чтобы заставить это выглядеть красиво, и показать , как min()
и max()
вместе , чтобы скопировать и вставить здесь проще. :)
Обновление 25 апреля 2020 года. Я также добавил Раздел 3, чтобы показать, как это можно сделать с помощью шаблонов C ++, в качестве ценного сравнения для тех, кто изучает и C, и C ++, или переходя от одного к другому. Я приложил все усилия, чтобы быть тщательным, основанным на фактах и правильным, чтобы сделать этот ответ каноническим справочником, к которому я могу возвращаться снова и снова, и я надеюсь, что вы найдете его таким же полезным, как и я.
Этот метод обычно используется, его уважают те, кто знает, как правильно его использовать, «фактический» способ ведения дел, и его можно использовать при правильном использовании, но с ошибками (подумайте: побочный эффект двойной оценки ), если вы когда-либо передать выражения, включая присвоение переменной, для сравнения:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
Этот метод позволяет избежать вышеупомянутых «двойных оценок» побочных эффектов и ошибок и поэтому считается лучшим, более безопасным и «более современным» GCC C способом сделать это. Ожидайте, что он будет работать как с компиляторами gcc, так и clang, поскольку clang по своей конструкции совместим с gcc (см. Примечание clang внизу этого ответа).
НО: ДЕЙСТВИТЕЛЬНО остерегайтесь эффектов « затенения переменных », поскольку выражения операторов, по-видимому, встроены и, следовательно, НЕ имеют своей собственной области видимости локальной переменной!
#define max(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; \
})
#define min(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; \
})
Обратите внимание, что в выражениях операторов gcc последнее выражение в блоке кода - это то, что «возвращается» из выражения, как если бы оно было возвращено из функции. Документация GCC гласит это так:
Последним в составном операторе должно быть выражение, за которым следует точка с запятой; значение этого подвыражения служит значением всей конструкции. (Если вы используете какой-то другой вид оператора последним в фигурных скобках, конструкция имеет тип void и, следовательно, фактически не имеет значения.)
Примечание C ++: при использовании C ++ шаблоны, вероятно, рекомендуются вместо этого типа конструкции, но я лично не люблю шаблоны и, вероятно, буду использовать одну из вышеупомянутых конструкций в C ++, так как я часто использую и предпочитаю стили C во встроенном C ++.
Этот раздел добавлен 25 апреля 2020 года:
За последние несколько месяцев я занимался кучей C ++, и в сообществе C ++, где это возможно, давление на шаблоны по сравнению с макросами, где это возможно, достаточно сильное. В результате я стал лучше пользоваться шаблонами и хочу добавить сюда полные версии шаблонов C ++ и сделать этот ответ более каноническим и исчерпывающим.
Вот какие основные шаблоны функции версии max()
и min()
может выглядеть в C ++:
template <typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
template <typename T>
T min(T a, T b)
{
return a < b ? a : b;
}
Дополнительное чтение о шаблонах C ++ здесь: Wikipedia: Template (C ++) .
Тем не менее, оба max()
и min()
уже являются частью стандартной библиотеки C ++, в <algorithm>
header ( #include <algorithm>
). В стандартной библиотеке C ++ они определены немного иначе, чем у меня выше. Прототипы по умолчанию для std::max<>()
и std::min<>()
, например, в C ++ 14, если посмотреть их прототипы в ссылках cplusplus.com чуть выше:
template <class T>
constexpr const T& max(const T& a, const T& b);
template <class T>
constexpr const T& min(const T& a, const T& b);
Обратите внимание, что ключевое слово typename
является псевдонимом class
(поэтому их использование идентично, говорите ли вы <typename T>
или <class T>
), поскольку после изобретения шаблонов C ++ позже было признано, что тип шаблона может быть обычным типом ( int
, float
и т. Д.), А не только тип класса.
Здесь вы можете видеть, что оба типа ввода, а также тип возвращаемого значения const T&
, что означает «постоянная ссылка на тип T
». Это означает, что входные параметры и возвращаемое значение передаются по ссылке, а не по значению . Это похоже на прохождение указателями и более эффективно для больших типов, таких как объекты классов. constexpr
Часть функции модифицирует сама функция и указывает , что функция должна быть способна оценивается во время компиляции (по крайней мере , если предусмотренных constexpr
входных параметров), но если она не может быть оценена во время компиляции, то он по умолчанию обратно в оценка во время выполнения, как и любая другая нормальная функция.
Аспект времени компиляции constexpr
функции C ++ делает ее своего рода C-макро-подобной, в том случае, если для constexpr
функции возможна оценка во время компиляции , это будет сделано во время компиляции, так же, как могла бы быть подстановка MIN()
или MAX()
быть полностью оценены во время компиляции в C или C ++ тоже. Дополнительные ссылки на эту информацию шаблона C ++ см. Ниже.
Clang note из Википедии :
[Clang] предназначен для использования в качестве замены для коллекции компиляторов GNU (GCC), поддерживая большинство его флагов компиляции и неофициальных расширений языка.
Стоит отметить, я думаю, что если вы определите 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)
fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
Похоже, что Windef.h
(а-ля #include <windows.h>
) есть макросы max
и min
(строчные) макросы, которые также страдают от трудности «двойной оценки», но они там для тех, кто не хочет перекатывать свои собственные :)
Я знаю, что парень сказал "C" ... Но если у вас есть возможность, используйте шаблон C ++:
template<class T> T min(T a, T b) { return a < b ? a : b; }
Введите safe, и никаких проблем с ++, упомянутых в других комментариях.
Максимум двух целых a
и b
есть (int)(0.5((a+b)+abs(a-b)))
. Это также может работать с (double)
и fabs(a-b)
для двойников (аналогично для поплавков)
Самый простой способ - определить ее как глобальную функцию в .h
файле и вызывать ее в любое время, если ваша программа имеет модульную структуру с большим количеством файлов. Если нет, double MIN(a,b){return (a<b?a:b)}
это самый простой способ.
warning: expression with side-effects multiply evaluated by macro
в месте использования ...