MIN und MAX in C.

300

Wo sind MINund MAXin C definiert, wenn überhaupt?

Was ist der beste Weg, um diese so allgemein und sicher wie möglich zu implementieren? (Compiler-Erweiterungen / Builtins für Mainstream-Compiler werden bevorzugt.)

Matt Joiner
quelle

Antworten:

392

Wo sind MINund MAXin C definiert, wenn überhaupt?

Sie sind nicht.

Was ist der beste Weg, um diese so allgemein und typsicher wie möglich zu implementieren (Compiler-Erweiterungen / Builtins für Mainstream-Compiler bevorzugt).

Als Funktionen. Ich würde keine Makros wie verwenden #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), insbesondere wenn Sie vorhaben, Ihren Code bereitzustellen. Schreiben Sie entweder Ihre eigenen, verwenden Sie so etwas wie Standard fmaxoder fminoder korrigieren Sie das Makro mithilfe des GCC-Typs (Sie erhalten auch einen Typensicherheitsbonus) in einem GCC-Anweisungsausdruck :

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

Alle sagen: "Oh, ich weiß über doppelte Evaluierung Bescheid, das ist kein Problem." Ein paar Monate später werden Sie stundenlang die albernsten Probleme beheben.

Beachten Sie die Verwendung von __typeof__anstelle von typeof:

Wenn Sie eine Header-Datei schreiben, die funktionieren muss, wenn sie in ISO C-Programmen enthalten ist, schreiben Sie __typeof__statt typeof.

David Titarenco
quelle
68
Weißt du, es wäre ziemlich praktisch, wenn gcc eine Warnung in der Art hätte: warning: expression with side-effects multiply evaluated by macroam Ort der Verwendung ...
Café
23
@caf: Wäre das nicht erforderlich, dass der Präprozessor die C-Syntax komplizierter kennt?
Dreamlax
3
Nach vielen Versuchen, dies herauszufinden, glaube ich nicht, dass dies in VC ++ ohnehin möglich ist. Am besten versuchen Sie jedoch, mit dem neuen decltypeSchlüsselwort MSVC ++ 2010 herumzuspielen. Trotzdem kann Visual Studio keine zusammengesetzten Anweisungen in Makros ausführen (und decltypeist sowieso C ++), dh GCCs ({ ... })Syntax, also bin ich mir ziemlich sicher, dass es sowieso nicht möglich ist. Ich habe mir keine anderen Compiler zu diesem Thema angesehen, sorry Luther: S
David Titarenco
7
@dreamlax Ich habe einmal einen Fall gesehen, in dem jemand MAX(someUpperBound, someRandomFunction())einen zufälligen Wert auf eine Obergrenze beschränkt hatte. Es war eine schreckliche Idee, aber es funktionierte auch nicht einmal, da das von MAXihm verwendete Problem mit der doppelten Bewertung bestand und er eine andere Zufallszahl als die ursprünglich bewertete hatte.
Zev Eisenberg
8
@Soumen Wenn Sie beispielsweise MIN(x++, y++)den Präprozessor aufrufen, wird der folgende Code generiert (((x++) < (y++)) ? (x++) : (y++)). Also, xund ywird zweimal erhöht.
Antonio
91

Es ist auch in den Versionen GNU libc (Linux) und FreeBSD von sys / param.h enthalten und hat die Definition von dreamlax.


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

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

Die Quell-Repositorys sind hier:

Mikel
quelle
Ich habe die Definitionen der Systeme, auf die ich Zugriff habe, in meiner obigen Antwort hinzugefügt (das Kommentarfeld akzeptiert keine Formatierung, soweit ich das beurteilen kann). Ich werde versuchen, die Links zu den FreeBSD / Linux / glibc-Quellrepos zu finden.
Mikel
+1. Sehr schön. Funktioniert für openSUSE/Linux 3.1.0-1.2-desktop/ gcc version 4.6.2 (SUSE Linux) auch. :) Schlecht, es ist nicht tragbar.
Jack
Funktioniert auch auf Cygwin.
CMCDragonkai
1
Moment mal. Es verhindert nicht die doppelte Bewertung, oder? : 3
user1857492
76

Es gibt ein std::minund std::maxin C ++, aber AFAIK, es gibt kein Äquivalent in der C-Standardbibliothek. Sie können sie selbst mit Makros wie definieren

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

Dies verursacht jedoch Probleme, wenn Sie so etwas schreiben MAX(++a, ++b).

dan04
quelle
10
warum zu viele Klammern setzen ??? Ich fand ein Quiz, in dem sie sagten, es #define MIN(A, B) ((A < B) ? A : B)sei kein flexibler Weg, warum ???
78
@Makouda: Zusätzliche Klammern in Makros helfen, Probleme mit der Priorität des Operators zu vermeiden. Betrachten Sie zum Beispiel #define MULT(x, y) x * y. Erweitert MULT(a + b, a + b)sich dann zu a + b * a + b, was a + (b * a) + bnach Vorrang analysiert wird . Das hat der Programmierer wahrscheinlich nicht beabsichtigt.
Dan04
das wird nicht benötigt wann ?: hat sowieso die niedrigste Priorität
Winger Sendon
@WingerSendon: Das tut es nicht; Der Komma-Operator tut dies.
Dan04
24

Vermeiden Sie nicht standardmäßige Compiler-Erweiterungen und implementieren Sie sie als vollständig typsicheres Makro in reinem Standard C (ISO 9899: 2011).

Lösung

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

Verwendung

MAX(int, 2, 3)

Erläuterung

Das Makro MAX erstellt basierend auf dem typeParameter ein weiteres Makro . Wenn dieses Steuermakro für den angegebenen Typ implementiert ist, wird überprüft, ob beide Parameter vom richtigen Typ sind. Wenn das typenicht unterstützt wird, liegt ein Compilerfehler vor.

Wenn entweder x oder y nicht vom richtigen Typ ist, liegt ein Compilerfehler in den ENSURE_Makros vor. Weitere solche Makros können hinzugefügt werden, wenn mehr Typen unterstützt werden. Ich habe angenommen, dass nur arithmetische Typen (Ganzzahlen, Gleitkommazahlen, Zeiger usw.) verwendet werden und keine Strukturen oder Arrays usw.

Wenn alle Typen korrekt sind, wird das Makro GENERIC_MAX aufgerufen. Für jeden Makroparameter sind zusätzliche Klammern erforderlich, wie beim Schreiben von C-Makros üblich.

Dann gibt es die üblichen Probleme mit impliziten Typ-Promotions in C. Der ?:Operator gleicht den 2. und 3. Operanden gegeneinander aus. Zum Beispiel GENERIC_MAX(my_char1, my_char2)wäre das Ergebnis von ein int. Um zu verhindern, dass das Makro solche potenziell gefährlichen Typwerbung durchführt, wurde ein endgültiger Typ verwendet, der auf den beabsichtigten Typ umgewandelt wurde.

Begründung

Wir möchten, dass beide Parameter des Makros vom gleichen Typ sind. Wenn einer von ihnen von einem anderen Typ ist, ist das Makro nicht mehr typsicher, da ein Operator wie ?:implizite Typ-Promotions liefert. Und weil dies der Fall ist, müssen wir das Endergebnis immer wieder auf den beabsichtigten Typ zurücksetzen, wie oben erläutert.

Ein Makro mit nur einem Parameter hätte viel einfacher geschrieben werden können. Bei zwei oder mehr Parametern muss jedoch ein zusätzlicher Typparameter eingefügt werden. Weil so etwas leider unmöglich ist:

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

Das Problem ist, dass, wenn das obige Makro wie MAX(1, 2)bei zwei aufgerufen intwird, immer noch versucht wird, alle möglichen Szenarien der _GenericZuordnungsliste zu erweitern. Das ENSURE_floatMakro wird also auch erweitert, obwohl es für nicht relevant ist int. Und da dieses Makro absichtlich nur den floatTyp enthält , wird der Code nicht kompiliert.

Um dies zu lösen, habe ich den Makronamen stattdessen während der Vorprozessorphase mit dem Operator ## erstellt, damit kein Makro versehentlich erweitert wird.

Beispiele

#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;
}
Lundin
quelle
Dieses GENERIC_MAXMakro ist übrigens eine schlechte Idee, man muss nur GENERIC_MAX(var++, 7)herausfinden, warum :-) Heutzutage (insbesondere bei stark optimierten / inlinierenden Compilern) sollten Makros so ziemlich nur auf die einfachen Formulare verwiesen werden. Die funktionsähnlichen sind besser als Funktionen und die Wertegruppen besser als Aufzählungen.
Paxdiablo
21

Ich denke nicht, dass es sich um standardisierte Makros handelt. Es gibt bereits standardisierte Funktionen für Gleitkommazahlen fmaxund fmin(und für Gleitkommazahlen fmaxfund fmaxlfür lange Doppelbilder).

Sie können sie als Makros implementieren, solange Sie sich der Probleme mit Nebenwirkungen / Doppelbewertung bewusst sind.

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

In den meisten Fällen können Sie es dem Compiler überlassen, zu bestimmen, was Sie tun möchten, und es so gut wie möglich optimieren. Während dies Probleme verursacht, wenn es wie verwendet wird MAX(i++, j++), bezweifle ich, dass es jemals notwendig ist, das Maximum der inkrementierten Werte auf einmal zu überprüfen. Zuerst inkrementieren, dann prüfen.

Dreamlax
quelle
Dies sollte die bevorzugte Antwort sein, da es eindeutig Min-
imranal
@imranal Worüber sprichst du genau? Der Implementierungscode dieser Bibliothek? Dieser Code wird jedoch nicht angezeigt , dh er wird nicht in die Benutzeroberfläche der Bibliothek eingefügt, da er möglicherweise unsicher ist.
Antonio
@Antonio Ich denke, Sie verwenden falsche Definitionen von "ausgesetzt" und "Schnittstelle". Die Schnittstelle der AC-Bibliothek sind die externen Variablen, Typen, Makros und Funktionsdeklarationen in einer Header-Datei. fmin / fmax werden in der Header-Datei deklariert, sodass sie als verfügbar bezeichnet werden. Ich bin mir nicht sicher, was Sie als unsicher bezeichnen.
Rationalcoder
21

Dies ist eine späte Antwort aufgrund einer relativ jungen Entwicklung. Da das OP die Antwort akzeptiert hat, die auf einer nicht portablen GCC- (und Clang-) Erweiterung beruht typeof- oder __typeof__für 'sauberes' ISO C -, gibt es ab gcc-4.9 eine bessere Lösung .

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

Der offensichtliche Vorteil dieser Erweiterung besteht darin, dass jedes Makroargument im Gegensatz zur __typeof__Lösung nur einmal erweitert wird.

__auto_typeist eine begrenzte Form von C ++ 11 auto. Es kann (oder sollte nicht?) Nicht in C ++ - Code verwendet werden, obwohl es keinen guten Grund gibt, die überlegenen Typinferenzfunktionen von autoC ++ 11 nicht zu verwenden .

Ich gehe jedoch davon aus, dass es bei Verwendung dieser Syntax keine Probleme gibt, wenn das Makro in einem extern "C" { ... }Bereich enthalten ist. zB aus einem C-Header. AFAIK, diese Erweiterung hat ihren Weg nicht gefunden

Brett Hale
quelle
Bezogen auf Brett Hales Kommentar , clangbegann __auto_typeum 2016 mit der Unterstützung (siehe Patch ).
Lars
Ein großes Lob für das Erkennen des Makroproblems, aber ich würde trotzdem davon ausgehen, dass eine Funktion wahrscheinlich besser wäre :-)
paxdiablo
@paxdiablo - Ich stimme zu, obwohl die Frage den c-preprocessorTag hat. Es ist nicht garantiert, dass eine Funktion auch mit diesem Schlüsselwort inline ist, es sei denn, Sie verwenden so etwas wie das __always_inline__Attribut von gcc .
Brett Hale
11

Ich habe diese Version geschrieben , die für MSVC, GCC, C und C ++ funktioniert.

#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
Matt Joiner
quelle
Ich habe positiv bewertet, aber Bezeichner, die mit einem Unterstrich gefolgt von einem Großbuchstaben beginnen, sind reserviert.
Dreamlax
8

Wenn Sie min / max benötigen, um eine teure Verzweigung zu vermeiden, sollten Sie den ternären Operator nicht verwenden, da er zu einem Sprung kompiliert wird. Der folgende Link beschreibt eine nützliche Methode zum Implementieren einer Min / Max-Funktion ohne Verzweigung.

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

cib
quelle
1
Wenn der Compiler klug genug ist, kann er den Zweig vermeiden
Axel Gneiting
2
Wenn die Optimierung aktiviert ist, geben alle modernen Compiler in den meisten Fällen eine bedingte Verschiebung anstelle einer Verzweigung aus, sodass die Verwendung solcher Hacks wenig Sinn macht.
Krzysztof Kosiński
1
Absolut wahr, ich habe keine Ahnung, was ich damals sah, es ist eine Weile her. Sowohl gcc als auch clang vermeiden Verzweigungen mit -O, sowohl auf x86 als auch auf armv7a.
Cib
6

@ David Titarenco genagelt es hier , aber lassen Sie mich zumindest sauber es sich ein wenig machen es schön aussehen, und beide zeigen min() und max() gemeinsam zu kopieren zu machen und Einfügen von hier leichter. :) :)

Update 25. April 2020: Ich habe auch einen Abschnitt 3 hinzugefügt, um zu zeigen, wie dies auch mit C ++ - Vorlagen geschehen würde, als wertvoller Vergleich für diejenigen, die sowohl C als auch C ++ lernen oder von einem zum anderen wechseln. Ich habe mein Bestes getan, um gründlich und sachlich und korrekt zu sein und diese Antwort zu einer kanonischen Referenz zu machen, auf die ich immer wieder zurückkommen kann, und ich hoffe, Sie finden sie genauso nützlich wie ich.

1. Der alte C-Makro-Weg:

Diese Technik wird häufig verwendet und von denjenigen, die wissen, wie man sie richtig anwendet, der "De-facto" -Methode, und bei sachgemäßer Anwendung gut anzuwenden, aber fehlerhaft (denken Sie: Nebeneffekt bei doppelter Bewertung ) Übergeben Sie jemals Ausdrücke einschließlich Variablenzuweisung , um zu vergleichen:

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

2. Der neue und verbesserte gcc " Anweisungsausdruck " Weg:

Diese Technik vermeidet die oben genannten Nebenwirkungen und Fehler bei der "doppelten Bewertung" und wird daher als die überlegene, sicherere und "modernere" GCC C-Methode angesehen, um dies zu tun. Erwarten Sie, dass es sowohl mit dem gcc- als auch mit dem clang-Compiler funktioniert, da clang von Natur aus gcc-kompatibel ist (siehe den clang-Hinweis am Ende dieser Antwort).

ABER: Achten Sie immer noch auf " Variable Shadowing " -Effekte, da Anweisungsausdrücke anscheinend inline sind und daher KEINEN eigenen lokalen Variablenbereich haben!

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

Beachten Sie, dass in gcc-Anweisungsausdrücken der letzte Ausdruck im Codeblock das ist, was vom Ausdruck "zurückgegeben" wird, als ob er von einer Funktion zurückgegeben worden wäre. In der Dokumentation von GCC heißt es so:

Das Letzte in der zusammengesetzten Anweisung sollte ein Ausdruck sein, gefolgt von einem Semikolon. Der Wert dieses Unterausdrucks dient als Wert des gesamten Konstrukts. (Wenn Sie eine andere Art von Anweisung verwenden, die zuletzt in geschweiften Klammern steht, hat das Konstrukt den Typ void und somit praktisch keinen Wert.)

3. Die C ++ - Vorlagenmethode:

C ++ Hinweis: Wenn Sie C ++ verwenden, werden Vorlagen wahrscheinlich stattdessen für diese Art von Konstrukt empfohlen, aber ich persönlich mag keine Vorlagen und würde wahrscheinlich sowieso eines der oben genannten Konstrukte in C ++ verwenden, da ich häufig C-Stile in eingebettetem C ++ verwende und bevorzuge.

Dieser Abschnitt wurde am 25. April 2020 hinzugefügt:

Ich habe in den letzten Monaten eine Menge C ++ gemacht, und der Druck, in der C ++ - Community Vorlagen gegenüber Makros zu bevorzugen, wo dies möglich ist, ist ziemlich groß. Infolgedessen kann ich Vorlagen besser verwenden und möchte der Vollständigkeit halber die C ++ - Vorlagenversionen hier einfügen, um eine kanonischere und gründlichere Antwort zu erhalten.

Hier ist, wie grundlegende Funktionsvorlagenversionen von C ++ aussehen max()und min()aussehen könnten:

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

Lesen Sie hier mehr über C ++ - Vorlagen: Wikipedia: Vorlage (C ++) .

Beide max()und min()sind jedoch bereits Teil der C ++ - Standardbibliothek im <algorithm>header ( #include <algorithm>). In der C ++ - Standardbibliothek sind sie etwas anders definiert als ich sie oben habe. Die Standardprototypen für std::max<>()und std::min<>()zum Beispiel in C ++ 14, die ihre Prototypen in den oben genannten Links zu cplusplus.com betrachten, sind:

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

Beachten Sie, dass das Schlüsselwort typenameist ein Alias für class(so ihre Nutzung ist identisch , ob Sie sagen , <typename T>oder <class T>), da sie später nach der Erfindung der C ++ Templates anerkannt wurde, dass die Template - Typ ein regulärer Typ sein könnte ( int, floatusw.) statt nur ein Klassentyp.

Hier können Sie sehen, dass sowohl die Eingabetypen als auch der Rückgabetyp const T&"konstante Referenz zum Typ T" sind. Dies bedeutet, dass die Eingabeparameter und der Rückgabewert als Referenz anstatt als Wert übergeben werden . Dies ähnelt dem Übergeben von Zeigern und ist für große Typen wie Klassenobjekte effizienter. Der constexprTeil der Funktion ändert die Funktion selbst und gibt an, dass die Funktion zur Kompilierungszeit ausgewertet werden kann (zumindest wenn constexprEingabeparameter angegeben sind). Wenn sie jedoch zur Kompilierungszeit nicht ausgewertet werden kann, wird standardmäßig a verwendet Laufzeitauswertung, wie bei jeder anderen normalen Funktion.

Der Aspekt der Kompilierungszeit einer constexprC ++ - Funktion macht sie zu einer Art C-Makro, da die Auswertung zur Kompilierungszeit für eine constexprFunktion zur Kompilierungszeit erfolgt, genau wie dies bei einer MIN()oder einer MAX()Makrosubstitution möglich wäre zur Kompilierungszeit auch in C oder C ++ vollständig ausgewertet werden. Weitere Referenzen zu diesen C ++ - Vorlageninformationen finden Sie unten.

Verweise:

  1. https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof
  2. https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
  3. MIN und MAX in C.
  4. Zusätzliche C ++ - Vorlagenreferenzen hinzugefügt im April 2020:
    1. ***** Wikipedia: Vorlage (C ++) <- GROSSE zusätzliche Informationen zu C ++ - Vorlagen!
    2. (Meine eigene Frage & Antwort): Warum ist `constexpr` Teil des C ++ 14-Template-Prototyps für` std :: max ()`?
    3. Unterschied zwischen "constexpr" und "const"

Clang-Notiz aus Wikipedia :

[Clang] wurde als Ersatz für die GNU Compiler Collection (GCC) entwickelt und unterstützt die meisten Kompilierungsflags und inoffiziellen Spracherweiterungen.

Gabriel Staples
quelle
An den Downvoter der letzten 24 Stunden: Gute Nachrichten! Ich habe meinen Abschnitt 4-Rant entfernt, den ich gestern mit Abschnitt 3 hinzugefügt habe, und ich habe ihn stattdessen hier eingefügt . Sie können meine Antwort gerne neu bewerten und bitte eine positive Bewertung abgeben, da ich viele gute Informationen hineingesteckt und mein Bestes getan habe, um daraus eine solide, hilfreiche und kanonische Antwort zu machen, von der alle profitieren. Es ist jetzt wieder konzentriert. :) Vielen Dank!
Gabriel Staples
4

Es ist erwähnenswert, dass ich denke, wenn Sie definieren minund maxmit dem Tertiär wie

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

dann das gleiche Ergebnis für den Sonderfall zu bekommen fmin(-0.0,0.0)und fmax(-0.0,0.0)Sie müssen die Argumente tauschen

fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
Z Boson
quelle
Funktioniert immer noch nicht für NaN. fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
Greggo
@greggo, ich habe hier eine bessere Antwort gegeben stackoverflow.com/a/30915238/2542702
Z Boson
4

Sieht aus wie Windef.h(a la #include <windows.h>) hat maxund min(Kleinbuchstaben) Makros, die auch unter der Schwierigkeit "doppelte Bewertung" leiden, aber sie sind für diejenigen da, die ihre eigenen nicht neu rollen wollen :)

Rogerdpack
quelle
12
Bist du überhaupt überrascht?
Matt Joiner
2

Ich weiß, dass der Typ "C" gesagt hat ... Aber wenn Sie die Chance haben, verwenden Sie eine C ++ - Vorlage:

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

Geben Sie safe ein und keine Probleme mit dem in anderen Kommentaren erwähnten ++.

Bas Kuenen
quelle
16
Argumente sollten konstante Referenzen sein, Sie wissen nie, welcher Benutzer übergeben wird.
nmikhailov
6
Eine solche Funktion wurde bereits standardisiert ( std :: min ).
Dreamlax
C ++ hat viele Standardfunktionen für die meisten normalen Zwecke. Erfinden Sie das Rad nicht neu. MS definiert jedoch auch ihre eigenen Min / Max, was manchmal Probleme verursacht
phuclv
0

Das Maximum von zwei ganzen Zahlen aund bist (int)(0.5((a+b)+abs(a-b))). Dies kann auch mit (double)und fabs(a-b)für Doppel funktionieren (ähnlich für Floats)

NRZ
quelle
Entschuldigung, wenn dies falsch ist, ich bin ein C-Anfänger, aber dieser Code funktioniert für mich
NRZ
2
Ich bin nicht sicher, ob es mit Nicht-Ganzzahlen funktioniert. Gleitkomma-Mathematik hat eine nichtlineare Genauigkeit.
Treesrule14
So erweitern Sie den Kommentar von @ Treesrule14: Dies funktioniert nicht, da Computer Zahlen nicht wie Mathematiker behandeln. Gleitkomma hat Rundungsprobleme, sodass Sie wahrscheinlich nicht die richtige Antwort erhalten. Selbst wenn Sie ganzzahlige Mathematik verwenden, ergibt MAX_INT + MAX_INT -2, sodass max (MAX_INT, MAX_INT) unter Verwendung Ihrer Formel als -1 ausgegeben wird.
user9876
-3

Am einfachsten ist es, sie als globale Funktion in einer .hDatei zu definieren und sie jederzeit aufzurufen, wenn Ihr Programm modular mit vielen Dateien ist. Wenn nicht, double MIN(a,b){return (a<b?a:b)}ist der einfachste Weg.

Srezat
quelle
1
@technosaurus Es wäre hilfreich, wenn Sie beschreiben würden, warum diese Lösung falsch ist, nicht nur, dass sie falsch ist.
Tur1ng
@technosaurus, Ihre Antwort ist wirklich nicht hilfreich. Tur1ing scheint, dass die Funktion völlig falsch definiert ist (fehlende Typen in Eingabeparametern, fehlendes Semikolon nach der return-Anweisung), und das Konvertieren von int-Eingaben in double ist eine schlechte Methode, daher sollte der Typ nicht double sein. Ein Definitions- oder Anweisungsausdruck wäre hier besser (z. B. hier ). Wenn es sich jedoch um eine Funktion handelt, sollten Sie eine Funktion für int32_t-Typen, eine für uint32_t-Typen und eine für float- oder double-Typen für insgesamt 3 festlegen verschiedene Funktionen.
Gabriel Staples
1
@GabrielStaples Diese Antwort sollte als keine Antwort gekennzeichnet werden - es gibt keine Hilfe. Obwohl es als Beispiel dafür dienen könnte, wie man auf kleinstem Raum am meisten falsch liegt. Das Empfehlen globaler Funktionen in einem Header (nicht einmal statisch inline?) Bricht Code mit 2+ Kompilierungseinheiten, kompiliert nicht einmal, benennt eine Funktion wie ein Makro, impliziert Ints wie 1989 und gibt ohne Angabe von Gründen ein Double zurück Casts, die bestenfalls Warnungen auslösen ... und vor allem beantwortet es die Frage nicht - nicht generisch, nicht typsicher und definitiv nicht der beste Weg
Technosaurus
Jedes dieser Probleme verdient weitere Kritik, die nicht ausreichend detailliert behandelt werden kann.
Technosaurus