Gdzie są MIN
i MAX
zdefiniowane w C, jeśli w ogóle?
Jaki jest najlepszy sposób na ich wdrożenie, tak ogólnie i bezpiecznie, jak to możliwe? (Preferowane są rozszerzenia / wbudowane kompilatory kompilatorów głównego nurtu).
źródło
Gdzie są MIN
i MAX
zdefiniowane w C, jeśli w ogóle?
Jaki jest najlepszy sposób na ich wdrożenie, tak ogólnie i bezpiecznie, jak to możliwe? (Preferowane są rozszerzenia / wbudowane kompilatory kompilatorów głównego nurtu).
Gdzie są
MIN
iMAX
zdefiniowane w C, jeśli w ogóle?
Nie są.
Jaki jest najlepszy sposób na ich wdrożenie, tak ogólnie, jak to tylko możliwe i jak najbardziej bezpieczne (preferowane są rozszerzenia / wbudowane kompilatory dla głównych kompilatorów).
Jako funkcje. Nie używałbym takich makr #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
, zwłaszcza jeśli planujesz wdrożyć swój kod. Napisz własny, użyj czegoś standardowego fmax
lub fmin
popraw makro za pomocą GFF typeof (dostajesz premię za bezpieczeństwo typów) w wyrażeniu instrukcji GCC :
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
Wszyscy mówią „och, wiem o podwójnej ocenie, to nie problem”, a kilka miesięcy później będziesz debugować najgłupsze problemy przez wiele godzin.
Zwróć uwagę na użycie __typeof__
zamiast typeof
:
Jeśli piszesz plik nagłówkowy, który musi działać po włączeniu do programów ISO C, pisz
__typeof__
zamiasttypeof
.
warning: expression with side-effects multiply evaluated by macro
w punkcie użycia ...decltype
słowem kluczowym MSVC ++ 2010 - ale mimo to Visual Studio nie może wykonywać instrukcji złożonych w makrach (idecltype
tak czy inaczej to C ++), tj.({ ... })
składnia GCC, więc jestem prawie pewien, że i tak nie jest to możliwe. Nie patrzyłem na żadne inne kompilatory dotyczące tego problemu, przepraszam Luther: SMAX(someUpperBound, someRandomFunction())
aby ograniczyć losową wartość do pewnego górnego limitu. To był okropny pomysł, ale też nie zadziałał, ponieważ ten,MAX
którego używał, miał problem z podwójną oceną, więc skończył z inną losową liczbą niż ta, którą początkowo oceniano.MIN(x++, y++)
preprocesor wygeneruje następujący kod(((x++) < (y++)) ? (x++) : (y++))
. Tak,x
iy
będą zwiększane dwukrotnie.Jest on także dostępny w wersjach sys / param.h GNU libc (Linux) i FreeBSD i ma definicję dostarczoną przez dreamlax.
W systemie Debian:
Na FreeBSD:
Repozytoria źródłowe są tutaj:
źródło
openSUSE/Linux 3.1.0-1.2-desktop
/gcc version 4.6.2 (SUSE Linux)
też. :) Źle, to nie jest przenośne.Jest
std::min
istd::max
w C ++, ale AFAIK, nie ma odpowiednika w bibliotece standardowej C. Możesz je zdefiniować samodzielnie za pomocą makr takich jakAle to powoduje problemy, jeśli napiszesz coś takiego
MAX(++a, ++b)
.źródło
#define MIN(A, B) ((A < B) ? A : B)
nie jest elastyczny sposób, dlaczego ???#define MULT(x, y) x * y
. NastępnieMULT(a + b, a + b)
rozwija się doa + b * a + b
, który analizuje jakoa + (b * a) + b
ze względu na pierwszeństwo. Prawdopodobnie nie tak zamierzał programista.Unikaj niestandardowych rozszerzeń kompilatora i zaimplementuj je jako całkowicie bezpieczne makro typu w czystym standardzie C (ISO 9899: 2011).
Rozwiązanie
Stosowanie
Wyjaśnienie
Makro MAX tworzy kolejne makro na podstawie
type
parametru. To makro sterujące, jeśli zostało zaimplementowane dla danego typu, służy do sprawdzania, czy oba parametry są poprawnego typu. Jeślitype
nie jest obsługiwany, wystąpi błąd kompilatora.Jeśli x lub y nie jest poprawnego typu, w
ENSURE_
makrach wystąpi błąd kompilatora . Więcej takich makr można dodać, jeśli obsługiwanych jest więcej typów. Założyłem, że będą używane tylko typy arytmetyczne (liczby całkowite, zmiennoprzecinkowe, wskaźniki itp.), A nie struktury i tablice itp.Jeśli wszystkie typy są poprawne, makro GENERIC_MAX zostanie wywołane. Potrzebne są dodatkowe nawiasy wokół każdego parametru makra, jako zwykłe standardowe środki ostrożności podczas pisania makr C.
Potem są zwykłe problemy z niejawnymi promocjami typu w C.
?:
Operator równoważy drugi i trzeci operand względem siebie. Na przykład wynikiemGENERIC_MAX(my_char1, my_char2)
może byćint
. Aby zapobiec wykonywaniu potencjalnie niebezpiecznych promocji przez makro, zastosowano ostateczny rzut rzutowany na zamierzony typ.Racjonalne uzasadnienie
Chcemy, aby oba parametry makra były tego samego typu. Jeśli jedno z nich jest innego typu, makro nie jest już bezpieczne, ponieważ operator lubi
?:
zapewni awans typu pośredniego. A ponieważ tak jest, zawsze musimy rzutować końcowy wynik z powrotem na zamierzony typ, jak wyjaśniono powyżej.Makro z tylko jednym parametrem mogłoby zostać napisane w znacznie prostszy sposób. Ale przy 2 lub więcej parametrach konieczne jest dołączenie dodatkowego parametru typu. Ponieważ coś takiego jest niestety niemożliwe:
Problem polega na tym, że jeśli powyższe makro zostanie wywołane tak jak
MAX(1, 2)
dwaint
, nadal będzie próbowało rozwinąć makro wszystkie możliwe scenariusze z_Generic
listy skojarzeń. WięcENSURE_float
makro będzie się zbyt rozbudowany, choć nie dotyczyint
. A ponieważ to makro celowo zawiera tylkofloat
typ, kod nie zostanie skompilowany.Aby rozwiązać ten problem, utworzyłem nazwę makra podczas fazy preprocesora, za pomocą operatora ##, aby żadne makro nie zostało przypadkowo rozwinięte.
Przykłady
źródło
GENERIC_MAX
Nawiasem mówiąc, to makro jest złym pomysłem, musisz tylko spróbowaćGENERIC_MAX(var++, 7)
dowiedzieć się, dlaczego :-) W dzisiejszych czasach (szczególnie przy mocno kompilujących / optymalizujących kompilatorach) makra powinny być w zasadzie sprowadzane tylko do prostych formularzy. Funkcje podobne są lepsze jako funkcje, a grupy wartości lepsze jako wyliczenia.Nie sądzę, że są to standardowe makra. Istnieją już znormalizowane funkcje dla liczb zmiennoprzecinkowych
fmax
ifmin
(oraz dla liczb zmiennoprzecinkowychfmaxf
ifmaxl
długich podwójnych).Możesz je zaimplementować jako makra, o ile masz świadomość problemów związanych z efektami ubocznymi / podwójną oceną.
W większości przypadków możesz pozostawić to kompilatorowi, aby określić, co próbujesz zrobić, i zoptymalizować go najlepiej, jak to możliwe. Chociaż powoduje to problemy przy takim użyciu
MAX(i++, j++)
, wątpię, aby zawsze była potrzeba sprawdzania maksymalnej wartości przyrostowej za jednym razem. Najpierw zwiększ, a następnie sprawdź.źródło
To późna odpowiedź z powodu dość niedawnego rozwoju. Ponieważ PO zaakceptował odpowiedź, która opiera się na nieprzenośnym rozszerzeniu GCC (i clang)
typeof
- lub__typeof__
na „czystym” ISO C - dostępne jest lepsze rozwiązanie od gcc-4.9 .Oczywistą zaletą tego rozszerzenia jest to, że każdy argument makra jest rozszerzany tylko raz, w przeciwieństwie do
__typeof__
rozwiązania.__auto_type
jest ograniczoną formą C ++ 11auto
. Nie można go (lub nie należy?) Używać w kodzie C ++, choć nie ma żadnego dobrego powodu, aby nie korzystać z lepszych możliwości wnioskowania typuauto
przypadku używania C ++ 11.To powiedziawszy, zakładam , że nie ma problemów z użyciem tej składni, gdy makro jest zawarte w
extern "C" { ... }
zakresie; np. z nagłówka C. AFAIK, to rozszerzenie nie znalazło sposobu na kliknięcie informacjiźródło
clang
zaczął wspierać__auto_type
około 2016 roku (patrz łatka ).c-preprocessor
tag. Nie można zagwarantować, że funkcja zostanie wstawiona nawet z tym słowem kluczowym, chyba że użyje czegoś takiego jak__always_inline__
atrybut gcc .Napisałem tę wersję, która działa dla MSVC, GCC, C i C ++.
źródło
Jeśli potrzebujesz min / max, aby uniknąć kosztownej gałęzi, nie powinieneś używać operatora trójskładnikowego, ponieważ kompiluje się on do skoku. Poniższy link opisuje przydatną metodę implementacji funkcji min / max bez rozgałęziania.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
źródło
@David Titarenco przybity go tutaj , ale pozwól mi przynajmniej czyste go nieco, aby wyglądać dobrze i pokazać zarówno
min()
imax()
razem zrobić kopiując i wklejając stąd łatwiejsze. :)Aktualizacja 25 kwietnia 2020 r .: Dodałem również sekcję 3, aby pokazać, jak można by to zrobić z szablonami C ++, jako cenne porównanie dla osób uczących się zarówno C i C ++, lub przechodzących z jednego do drugiego. Zrobiłem co w mojej mocy, aby być dokładnym, rzeczowym i poprawnym, aby uczynić tę odpowiedź kanonicznym odniesieniem, do którego mogę powracać raz po raz, i mam nadzieję, że okaże się tak przydatna jak ja.
1. Stary sposób makro C:
Ta technika jest powszechnie stosowana, szanowana przez tych, którzy wiedzą, jak ją właściwie stosować, „de facto” sposób robienia rzeczy, i dobrze jest ją stosować, jeśli jest właściwie stosowana, ale buggy (pomyśl: efekt podwójnej oceny ), jeśli kiedykolwiek przekazuj wyrażenia, w tym przypisanie zmiennych, w celu porównania:
2. Nowy i ulepszony sposób „ wyrażenia instrukcji gcc” :
Ta technika pozwala uniknąć powyższych skutków ubocznych i błędów „podwójnej oceny” i dlatego jest uważana za lepszy, bezpieczniejszy i „bardziej nowoczesny” sposób GCC C. Spodziewaj się, że będzie działać zarówno z kompilatorami gcc, jak i clang, ponieważ clang jest z założenia zgodny z gcc (patrz uwaga clang na dole tej odpowiedzi).
ALE: W dalszym ciągu uważaj na efekty „ cieniowania zmiennych ”, ponieważ wyrażenia instrukcji są najwyraźniej wbudowane i dlatego NIE mają własnego zasięgu zmiennej lokalnej!
Zauważ, że w wyrażeniach instrukcji gcc ostatnie wyrażenie w bloku kodu jest tym, co jest „zwracane” z wyrażenia, tak jakby zostało zwrócone z funkcji. Dokumentacja GCC mówi to w ten sposób:
3. Sposób szablonu C ++:
Uwaga C ++: jeśli używam C ++, szablony są prawdopodobnie zalecane zamiast tego typu konstrukcji, ale ja osobiście nie lubię szablonów i prawdopodobnie użyłbym jednej z powyższych konstrukcji w C ++, ponieważ często używam i preferuję style C również we wbudowanym C ++.
W tej sekcji dodano 25 kwietnia 2020 r .:
W ciągu ostatnich kilku miesięcy robiłem mnóstwo C ++, a presja, by preferować szablony nad makrami, tam gdzie jest to możliwe, w społeczności C ++ jest dość silna. W rezultacie coraz lepiej używam szablonów i chcę tutaj umieścić wersje szablonów C ++, aby uzyskać kompletność i uczynić to bardziej kanoniczną i dokładną odpowiedzią.
Oto, co podstawowe szablon funkcji wersje
max()
imin()
może wyglądać jak w C ++:Przeczytaj dodatkowe informacje na temat szablonów C ++ tutaj: Wikipedia: Szablon (C ++) .
Jednak zarówno
max()
imin()
są już częścią biblioteki standardowej C ++, w<algorithm>
nagłówku (#include <algorithm>
). W standardowej bibliotece C ++ są one zdefiniowane nieco inaczej niż mam je powyżej. Domyślne prototypystd::max<>()
istd::min<>()
, na przykład, w C ++ 14, patrząc na ich prototypy w linkach cplusplus.com powyżej, to:Zauważ, że słowo kluczowe
typename
jest aliasemclass
(więc ich użycie jest identyczne bez względu na to, czy mówisz<typename T>
czy<class T>
), ponieważ po wynalezieniu szablonów C ++ potwierdzono, że typ szablonu może być typem regularnym (int
,float
, itd.), A nie tylko typ klasy.Tutaj możesz zobaczyć, że oba typy wejściowe, jak również typ zwracany, są
const T&
, co oznacza „stałe odniesienie do typuT
”. Oznacza to, że parametry wejściowe i wartość zwracana są przekazywane przez odniesienie zamiast przez wartość . To jest jak przekazywanie wskaźników i jest bardziej wydajne w przypadku dużych typów, takich jak obiekty klasy.constexpr
Część funkcji modyfikuje samą funkcję i wskazuje, że funkcja musi być w stanie być oceniany w czasie kompilacji (przynajmniej w przypadku określonychconstexpr
parametrów wejściowych), jednak jeśli nie można ocenić w czasie kompilacji, to domyślnie powrotem do ocena w czasie wykonywania, jak każda inna normalna funkcja.Aspekt czasu kompilacji
constexpr
funkcji C ++ sprawia, że jest ona trochę podobna do makropolecenia C, ponieważ jeśli ocenaconstexpr
funkcji kompilacji jest możliwa dla funkcji, zostanie ona wykonana w czasie kompilacji, podobnie jak podstawienie makraMIN()
lubMAX()
ewentualnie być w pełni oceniany podczas kompilacji również w C lub C ++. Aby uzyskać dodatkowe odniesienia do informacji o szablonie C ++, patrz poniżej.Bibliografia:
Notatka Clang z Wikipedii :
źródło
Warto zauważyć, że myślę, że jeśli zdefiniujesz
min
imax
z trzeciorzędowym, takim jaknastępnie, aby uzyskać ten sam wynik dla specjalnego przypadku
fmin(-0.0,0.0)
ifmax(-0.0,0.0)
musisz zamienić argumentyźródło
fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
Wygląda na to, że
Windef.h
(la#include <windows.h>
) mamax
imin
(małe litery) makra, które również cierpią z powodu trudności z „podwójną oceną”, ale są dostępne dla tych, którzy nie chcą ponownie tworzyć własnych :)źródło
Wiem, że facet powiedział „C” ... Ale jeśli masz szansę, użyj szablonu C ++:
Wpisz bezpieczny i bez problemów z ++ wspomnianym w innych komentarzach.
źródło
Maksymalnie dwie liczby całkowite
a
ib
to(int)(0.5((a+b)+abs(a-b)))
. Może to również działać z podwójnymi(double)
ifabs(a-b)
dla nich (podobnie dla pływaków)źródło
Najprostszym sposobem jest zdefiniowanie go jako funkcji globalnej w
.h
pliku i wywołanie go w dowolnym momencie, jeśli twój program jest modułowy z dużą ilością plików. Jeśli nie,double MIN(a,b){return (a<b?a:b)}
to najprostszy sposób.źródło