Где 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
.
warning: expression with side-effects multiply evaluated by macro
в месте использования ...decltype
ключевым словом MSVC ++ 2010 - но даже в этом случае Visual Studio не может выполнять составные операторы в макросах. (иdecltype
в любом случае это C ++), то есть({ ... })
синтаксис GCC, так что я уверен, что в любом случае это невозможно. Я не смотрел на другие компиляторы по этой проблеме, извините Лютер: SMAX(someUpperBound, someRandomFunction())
ограничить случайное значение до некоторой верхней границы. Это была ужасная идея, но она также не работала, потому чтоMAX
он использовал проблему двойной оценки, поэтому он получил случайное число, отличное от того, которое было первоначально оценено.MIN(x++, y++)
препроцессор, будет сгенерирован следующий код(((x++) < (y++)) ? (x++) : (y++))
. Итак,x
иy
будет увеличен в два раза.Он также предоставляется в версиях sys / param.h для GNU libc (Linux) и FreeBSD и имеет определение, предоставленное dreamlax.
На Debian:
На FreeBSD:
Исходные репозитории находятся здесь:
источник
openSUSE/Linux 3.1.0-1.2-desktop
/gcc version 4.6.2 (SUSE Linux)
тоже. :) Плохо, что это не портативно.Есть
std::min
иstd::max
в C ++, но AFAIK, в стандартной библиотеке C нет эквивалента. Вы можете определить их самостоятельно с помощью макросов, таких какНо это вызывает проблемы, если вы пишете что-то вроде
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).
Решение
использование
объяснение
Макрос MAX создает другой макрос на основе
type
параметра. Этот макрос управления, если он реализован для данного типа, используется для проверки того, что оба параметра имеют правильный тип. Еслиtype
не поддерживается, будет ошибка компилятора.Если x или y имеет неправильный тип, в
ENSURE_
макросах будет ошибка компилятора . Можно добавить больше таких макросов, если поддерживается больше типов. Я предполагал, что будут использоваться только арифметические типы (целые числа, числа с плавающей запятой, указатели и т. Д.), А не структуры или массивы и т. Д.Если все типы верны, будет вызван макрос GENERIC_MAX. Дополнительные скобки необходимы вокруг каждого параметра макроса, как обычная стандартная мера предосторожности при написании макросов Си.
Тогда есть обычные проблемы с неявным продвижением типов в C.
?:
Оператор уравновешивает 2-й и 3-й операнд друг против друга. Например, результатомGENERIC_MAX(my_char1, my_char2)
будетint
. Чтобы макрос не мог выполнять такие потенциально опасные продвижения типа, использовался финальный тип, приведенный к предполагаемому типу.обоснование
Мы хотим, чтобы оба параметра макроса были одного типа. Если один из них имеет другой тип, макрос больше не является типобезопасным, потому что оператор like
?:
будет выдавать неявные продвижения типа. И поскольку это так, мы также всегда должны приводить конечный результат обратно к намеченному типу, как описано выше.Макрос только с одним параметром мог бы быть написан намного проще. Но с 2 или более параметрами необходимо включить дополнительный параметр типа. Потому что что-то подобное, к сожалению, невозможно:
Проблема состоит в том, что если вышеупомянутый макрос вызывается как
MAX(1, 2)
с двумяint
, он все равно будет пытаться макрорасширить все возможные сценарии_Generic
списка ассоциаций. Таким образом,ENSURE_float
макрос также будет расширен, хотя он не имеет отношения кint
. А поскольку этот макрос намеренно содержит толькоfloat
тип, код не будет компилироваться.Чтобы решить эту проблему, я создал вместо этого имя макроса на этапе предварительной обработки с помощью оператора ##, чтобы ни один макрос случайно не раскрылся.
Примеры
источник
GENERIC_MAX
Кстати, этот макрос - плохая идея, вам нужно только попытатьсяGENERIC_MAX(var++, 7)
выяснить, почему :-) В настоящее время (особенно с сильно оптимизирующими / встроенными компиляторами), макросы в значительной степени следует относить только к простым формам. Подобные функции лучше как функции, а группы значений лучше как перечисления.Я не думаю, что это стандартизированные макросы. Уже есть стандартизированные функции для чисел с плавающей точкой
fmax
иfmin
(иfmaxf
для чисел с плавающей запятой, иfmaxl
для длинных двойных чисел ).Вы можете реализовать их как макросы, если вы знаете о побочных эффектах / двойной оценке.
В большинстве случаев вы можете оставить это на усмотрение компилятора, чтобы определить, что вы пытаетесь сделать, и оптимизировать его как можно лучше. Хотя это вызывает проблемы при использовании как
MAX(i++, j++)
, я сомневаюсь, что когда-либо есть необходимость в проверке максимума приращенных значений за один раз. Сначала увеличьте, затем проверьте.источник
Это поздний ответ из-за довольно недавнего развития событий. Поскольку OP принял ответ, основанный на непереносимом расширении GCC (и clang)
typeof
- или__typeof__
на «чистом» ISO C - для gcc-4.9 доступно лучшее решение .Очевидное преимущество этого расширения состоит в том, что каждый макро-аргумент раскрывается только один раз, в отличие от
__typeof__
решения.__auto_type
это ограниченная форма C ++ 11auto
. Он не может (или не должен?) Использоваться в коде C ++, хотя нет веской причины не использовать возможности превосходного вывода типовauto
при использовании C ++ 11.Тем не менее, я предполагаю, что нет никаких проблем с использованием этого синтаксиса, когда макрос включен в
extern "C" { ... }
область действия; например, из заголовка C. AFAIK, это расширение не нашло свой путьисточник
clang
начал поддерживать__auto_type
около 2016 года (см. Патч ).c-preprocessor
тег. Функция не гарантируется встроенной даже с указанным ключевым словом, если только не используется что-то вроде__always_inline__
атрибута gcc .Я написал эту версию, которая работает для MSVC, GCC, C и C ++.
источник
Если вам нужно мин / макс, чтобы избежать дорогостоящей ветки, вам не следует использовать троичный оператор, так как он скомпилируется до прыжка. Ссылка ниже описывает полезный метод для реализации функции min / max без ветвления.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
источник
@David Titarenco прибил его здесь , но позвольте мне , по крайней мере в чистоте его немного , чтобы заставить это выглядеть красиво, и показать , как
min()
иmax()
вместе , чтобы скопировать и вставить здесь проще. :)Обновление 25 апреля 2020 года. Я также добавил Раздел 3, чтобы показать, как это можно сделать с помощью шаблонов C ++, в качестве ценного сравнения для тех, кто изучает и C, и C ++, или переходя от одного к другому. Я приложил все усилия, чтобы быть тщательным, основанным на фактах и правильным, чтобы сделать этот ответ каноническим справочником, к которому я могу возвращаться снова и снова, и я надеюсь, что вы найдете его таким же полезным, как и я.
1. Старый способ макроса C:
Этот метод обычно используется, его уважают те, кто знает, как правильно его использовать, «фактический» способ ведения дел, и его можно использовать при правильном использовании, но с ошибками (подумайте: побочный эффект двойной оценки ), если вы когда-либо передать выражения, включая присвоение переменной, для сравнения:
2. Новый и улучшенный способ выражения выражения gcc :
Этот метод позволяет избежать вышеупомянутых «двойных оценок» побочных эффектов и ошибок и поэтому считается лучшим, более безопасным и «более современным» GCC C способом сделать это. Ожидайте, что он будет работать как с компиляторами gcc, так и clang, поскольку clang по своей конструкции совместим с gcc (см. Примечание clang внизу этого ответа).
НО: ДЕЙСТВИТЕЛЬНО остерегайтесь эффектов « затенения переменных », поскольку выражения операторов, по-видимому, встроены и, следовательно, НЕ имеют своей собственной области видимости локальной переменной!
Обратите внимание, что в выражениях операторов gcc последнее выражение в блоке кода - это то, что «возвращается» из выражения, как если бы оно было возвращено из функции. Документация GCC гласит это так:
3. Шаблон C ++:
Примечание C ++: при использовании C ++ шаблоны, вероятно, рекомендуются вместо этого типа конструкции, но я лично не люблю шаблоны и, вероятно, буду использовать одну из вышеупомянутых конструкций в C ++, так как я часто использую и предпочитаю стили C во встроенном C ++.
Этот раздел добавлен 25 апреля 2020 года:
За последние несколько месяцев я занимался кучей C ++, и в сообществе C ++, где это возможно, давление на шаблоны по сравнению с макросами, где это возможно, достаточно сильное. В результате я стал лучше пользоваться шаблонами и хочу добавить сюда полные версии шаблонов C ++ и сделать этот ответ более каноническим и исчерпывающим.
Вот какие основные шаблоны функции версии
max()
иmin()
может выглядеть в C ++:Дополнительное чтение о шаблонах C ++ здесь: Wikipedia: Template (C ++) .
Тем не менее, оба
max()
иmin()
уже являются частью стандартной библиотеки C ++, в<algorithm>
header (#include <algorithm>
). В стандартной библиотеке C ++ они определены немного иначе, чем у меня выше. Прототипы по умолчанию дляstd::max<>()
иstd::min<>()
, например, в C ++ 14, если посмотреть их прототипы в ссылках cplusplus.com чуть выше:Обратите внимание, что ключевое слово
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 из Википедии :
источник
Стоит отметить, я думаю, что если вы определите
min
иmax
с третичными, такими какзатем получить тот же результат для особого случая,
fmin(-0.0,0.0)
иfmax(-0.0,0.0)
вам нужно поменять местами аргументыисточник
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 ++:
Введите 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)}
это самый простой способ.источник