Frage

Wo sind MIN und MAX in C definiert, wenn über?

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

War es hilfreich?

Lösung

  

Wo sind MIN und MAX in C definiert, wenn über?

Sie sind es nicht.

  

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

Als Funktionen. Ich würde nicht Makros wie #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) verwendet, vor allem, wenn Sie Ihren Code zu implementieren. Entweder schreiben Sie Ihre eigene, Verwendung so etwas wie Standard fmax oder fmin oder fixieren Sie das Makro mit GCC typeof (Sie Typsicherheit Bonus bekommen auch):

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

Jeder sagt, „oh ich über doppelte Auswertung weiß, es ist kein Problem“ und ein paar Monate auf der Straße, werden Sie die dümmsten Probleme stundenlang Debuggen werden.

Beachten Sie die Verwendung von __typeof__ statt typeof:

  

Wenn Sie eine Header-Datei schreiben, dass   arbeiten müssen, wenn sie in ISO C enthalten   Programme, Schreib __typeof__ statt   typeof.

Andere Tipps

Es ist auch in dem GNU libc (Linux) und FreeBSD-Versionen von sys / param.h zur Verfügung gestellt und hat die Definition von dreamlax zur Verfügung gestellt.


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.

Unter 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-Repositories sind hier:

Vermeiden Sie Nicht-Standard-Compiler-Erweiterungen und implementieren sie als vollständig typsicher 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)

Erklärung

Das Makro MAX erzeugt ein anderes Makro basiert auf dem type Parameter. Diese Steuerung Makro, wenn für den angegebenen Typen implementiert, verwendet wird, um zu überprüfen, dass beiden Parameter vom richtigen Typ sind. Wenn die type nicht unterstützt wird, wird es ein Compiler-Fehler sein.

Wenn entweder x oder y nicht vom richtigen Typ ist, wird es ein Compiler-Fehler in dem ENSURE_ Makros sein. Mehr solche Makros können hinzugefügt werden, wenn mehr Arten unterstützt werden. Ich habe angenommen, dass nur arithmetische Typen (ganze Zahlen, die Schwimmer, Zeiger usw.) verwendet werden und nicht die Strukturen oder Arrays etc.

Wenn alle Typen korrekt sind, wird die GENERIC_MAX Makro aufgerufen werden. Extra Klammern sind um jeden Makroparameter benötigt, da die übliche Standard Vorsicht, wenn C-Makros zu schreiben.

Dann gibt es noch die üblichen Probleme mit impliziter Art Promotions in C. Die ?:operator der 2. balanciert und 3. Zeile gegeneinander an. Zum Beispiel würde das Ergebnis GENERIC_MAX(my_char1, my_char2) ein int sein. Um das Makro zu tun, solche potenziell gefährliche Art Promotions, eine endgültige Typumwandlung auf die vorgesehene Art zu verhindern verwendet wurde.

Begründung

Wir wollen beiden Parameter an das Makro vom gleichen Typ zu sein. Wenn einer von ihnen von einem anderen Typ ist, ist das Makro nicht mehr Typ sicher, da ein Bediener wie ?: impliziten Typ Promotions ergibt. Und weil es der Fall ist, wir auch immer müssen das Endergebnis wieder auf die vorgesehene Art werfen, wie oben erläutert.

Ein Makro mit nur einem Parameter in eine viel einfachere Art und Weise geschrieben worden sein könnte. Aber mit zwei oder mehreren Parametern, gibt es einen Bedarf einen zusätzlichen Typ-Parameter enthalten. Weil so etwas wie das ist leider nicht möglich:

// 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 die oben Makro als MAX(1, 2) mit zwei int genannt wird, ist es noch zu Makro erweitern alle möglichen Szenarien der _Generic Assoziationsliste versuchen. So wird das ENSURE_float Makro auch erweitert bekommen, auch wenn es nicht relevant für int ist. Und da bewusst, dass Makro nur den float Typen enthält, wird der Code nicht kompiliert werden.

Um dies zu lösen, habe ich den Makronamen während der Pre-Prozessor-Phase statt, mit dem ##-Operator, so dass 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;
}

Ich glaube nicht, dass sie standardisierte Makros ist. Es gibt standardisierte Funktionen für Floating-Point bereits, fmax und fmin (und fmaxf für Schwimmer und fmaxl für lange Doppelzimmer).

Sie können sie als Makros so lange implementieren, wie Sie wissen, von den Fragen der Nebenwirkungen / Doppel-Auswertung.

#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 an den Compiler verlassen, um zu bestimmen, was Sie versuchen zu tun und optimieren es, so gut er kann. Während diese Probleme verursacht, wenn wie MAX(i++, j++) verwendet, ich bezweifle, dass es bei der Prüfung der maximal erhöht Werte in einem Rutsch immer viel Bedarf. Increment zuerst, dann überprüfen.

Dies ist eine späte Antwort, aufgrund einer relativ neue Entwicklung. Da die OP die Antwort akzeptiert, die auf einer nicht tragbaren GCC (und Klappern) Erweiterung typeof beruht - oder __typeof__ für ‚saubere‘ ISO C - es gibt eine bessere Lösung als die gcc-4.9 .

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

Der offensichtliche Vorteil dieser Erweiterung ist, dass jedes Makro-Argument nur einmal erweitert wird, im Gegensatz zu der __typeof__ Lösung.

__auto_type ist eine begrenzte Form von C ++ 11 der auto. Es kann nicht (oder nicht?) In C ++ Code verwendet werden, obwohl es kein guter Grund ist nicht die überlegenen Typinferenz Fähigkeiten von auto zu verwenden bei der Verwendung von C ++ 11.

Das heißt, ich übernehmen gibt es keine Probleme mit dieser Syntax, wenn der Makro in einem extern "C" { ... } Umfang enthalten ist; beispielsweise von einem C-Header. AFAIK hat diese Erweiterung nicht seine Art und Weise Informationen Klirren

gefunden

Ich schrieb diese Version dass die Arbeiten für MSVC, GCC, C und 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

Wenn Sie min / max benötigen, um einen teueren Zweig zu vermeiden, sollten Sie nicht den ternären Operator verwenden, da es zu einem Sprung kompiliert nach unten wird. Der Link unten beschreibt eine nützliche Methode zur Implementierung einer Min- / Max-Funktion ohne Verzweigung.

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

Es ist erwähnenswert, ich denke, dass, wenn Sie definieren min und max mit dem tertiären wie

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

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

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

Sieht aus wie Windef.h (a la #include <windows.h>) hat max und min (Kleinschreibung) Makros, die auch von der „Doppelauswertung“ Schwierigkeiten leiden, aber sie sind für diejenigen, die es tun wollen, nicht zu re-Rolle ihre eigenen :)

weiß ich sagte der Typ „C“ ... 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; }

Typ sicher, und keine Probleme mit der ++ in anderen Kommentaren erwähnt.

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

Bezug zu Brett Hales Kommentar begann clang __auto_type um 2016 unterstützt (siehe Patch ).

Der einfachste Weg ist es als eine globale Funktion in einer .h Datei zu definieren, und es nennt, wann immer Sie wollen, wenn Ihr Programm mit vielen Dateien modular aufgebaut ist. Wenn nicht, ist double MIN(a,b){return (a<b?a:b)} der einfachste Weg.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top