min en max in C

Waar zijn minen maxgedefinieerd in C, indien überhaupt?

Wat is de beste manier om deze, net zo generiek en type veilig mogelijk te implementeren? (Compiler-extensies / ingebouwdeins voor mainstream-compilers die de voorkeur hebben.)


1, Autoriteit 100%

Waar zijn minen maxgedefinieerd in C, indien überhaupt?

ze zijn niet.

Wat is de beste manier om deze te implementeren, zo generiek en type veilig mogelijk (compiler-extensies / ingebouwdeins voor mainstream-compilers die de voorkeur hebben).

als functies. Ik zou geen macro’s gebruiken zoals #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), vooral als u van plan bent u te implementeren code. Schrijf je eigen, gebruik zoiets als standaard fmaxof fmin, of repareer de Macro met GCC’s Type-(u krijgt typety bonus te) in een gcc-instructie-expressie :

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

Iedereen zegt: “Oh ik weet over dubbele evaluatie, het is geen probleem” en een paar maanden langs de weg, u zult urenlang de silligste problemen debuggen.

Let op het gebruik van __typeof__in plaats van typeof:

Als u een header-bestand schrijft
moet werken wanneer opgenomen in ISO C
programma’s, schrijf __typeof__in plaats van
typeof.


Antwoord 2, autoriteit 23%

Het wordt ook geleverd in de GNU libc (Linux) en FreeBSD-versies van sys/param.h, en heeft de definitie van dreamlax.


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

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

De bronbronnen zijn hier:


Antwoord 3, autoriteit 20%

Er is een std::minen std::maxin C++, maar AFAIK, er is geen equivalent in de C-standaardbibliotheek. Je kunt ze zelf definiëren met macro’s zoals

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

Maar dit veroorzaakt problemen als je iets schrijft als MAX(++a, ++b).


Antwoord 4, autoriteit 6%

Vermijd niet-standaard compiler-extensies en implementeer deze als een volledig typeveilige macro in pure standaard C (ISO 9899:2011).

Oplossing

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

Gebruik

MAX(int, 2, 3)

Uitleg

De macro MAX maakt een andere macro op basis van de parameter type. Deze controlemacro, indien geïmplementeerd voor het gegeven type, wordt gebruikt om te controleren of beide parameters van het juiste type zijn. Als het typeniet wordt ondersteund, is er een compilerfout.

Als x of y niet van het juiste type is, zal er een compilerfout zijn in de ENSURE_macro’s. Er kunnen meer van dergelijke macro’s worden toegevoegd als er meer typen worden ondersteund. Ik ben ervan uitgegaan dat alleen rekenkundige typen (integers, floats, pointers enz.) zullen worden gebruikt en niet structs of arrays enz.

Als alle typen correct zijn, wordt de GENERIC_MAX macro aangeroepen. Er zijn extra haakjes nodig rond elke macroparameter, als de gebruikelijke standaardvoorzorgsmaatregel bij het schrijven van C-macro’s.

Dan zijn er de gebruikelijke problemen met impliciete typepromoties in C. De ?:operator brengt de 2e en 3e operand tegen elkaar in evenwicht. Het resultaat van GENERIC_MAX(my_char1, my_char2)zou bijvoorbeeld een intzijn. Om te voorkomen dat de macro zulke potentieel gevaarlijke typepromoties doet, is een definitieve typecast gebruikt voor het beoogde type.

Rationale

We willen dat beide parameters van de macro van hetzelfde type zijn. Als een van hen van een ander type is, is de macro niet langer typeveilig, omdat een operator als ?:impliciete typepromoties oplevert. En omdat dat zo is, moeten we ook altijd het uiteindelijke resultaat terug casten naar het beoogde type, zoals hierboven uitgelegd.

Een macro met slechts één parameter had op een veel eenvoudigere manier geschreven kunnen worden. Maar bij 2 of meer parameters is het nodig om een extra typeparameter op te nemen. Want zoiets is helaas onmogelijk:

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

Het probleem is dat als de bovenstaande macro wordt aangeroepen als MAX(1, 2)met twee int, het nog steeds zal proberen om alle mogelijke scenario’s van de _Genericassociatielijst. Dus de macro ENSURE_floatwordt ook uitgebreid, ook al is het niet relevant voor int. En aangezien die macro opzettelijk alleen het type floatbevat, wordt de code niet gecompileerd.

Om dit op te lossen, heb ik in plaats daarvan de macronaam gemaakt tijdens de pre-processorfase, met de ##-operator, zodat er geen macro per ongeluk wordt uitgebreid.

Voorbeelden

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

Antwoord 5, autoriteit 5%

Dit is een laat antwoord vanwege een vrij recente ontwikkeling. Aangezien de OP het antwoord accepteerde dat afhankelijk is van een niet-draagbare GCC (en clang) extensie typeof– of __typeof__voor ‘schone’ ISO C – is er een betere oplossing beschikbaar als van gcc-4.9.

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

Het duidelijke voordeel van deze extensie is dat elk macro-argument slechts één keer wordt uitgebreid, in tegenstelling tot de oplossing __typeof__.

__auto_typeis een beperkte vorm van C++11’s auto. Het kan (of mag niet?) worden gebruikt in C++-code, hoewel er geen goede reden is om de superieure type-inferentiemogelijkheden van autoniet te gebruiken bij het gebruik van C++11.

Dat gezegd hebbende, neem ik aandat er geen problemen zijn bij het gebruik van deze syntaxis wanneer de macro is opgenomen in een extern "C" { ... }scope; bijvoorbeeld van een C-header. AFAIK, deze extensie heeft zijn weg niet gevonden info clang


Antwoord 6, autoriteit 5%

Ik denk niet dat ze gestandaardiseerde macro’s zijn. Er zijn al gestandaardiseerde functies voor het drijvende punt, fmaxen fmin(EN fmaxfVOOR DIAKELS EN fmaxlVOOR LONG verdubbelt).

U kunt ze implementeren als macro’s zolang u op de hoogte bent van de problemen van bijwerkingen / dubbele evaluatie.

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

In de meeste gevallen kunt u het aan de compiler achterlaten om te bepalen wat u probeert te doen en het zo goed mogelijk te optimaliseren. Hoewel dit problemen veroorzaakt bij gebruik als MAX(i++, j++), twijfel ik of er ooit veel nodig is om het maximum van verhogende waarden in één keer te controleren. Verhoog eerst en controleer dan.


Antwoord 7, autoriteit 2%

Ik heb deze versiegeschreven dat werkt voor MSVC, GCC, C en 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

Antwoord 8, autoriteit 2%

Als je min/max nodig hebt om een dure vertakking te vermijden, moet je de ternaire operator niet gebruiken, omdat deze tot een sprong zal compileren. De onderstaande link beschrijft een handige methode voor het implementeren van een min/max-functie zonder vertakkingen.

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


Antwoord 9

Het is de moeite waard erop te wijzen dat ik denk dat als je minen maxdefinieert met de ternaire bewerking zoals

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

om hetzelfde resultaat te krijgen voor het speciale geval van fmin(-0.0,0.0)en fmax(-0.0,0.0)moet je de argumenten omwisselen

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

Antwoord 10

Het lijkt erop dat Windef.h(a la #include <windows.h>) maxen min(kleine letters) macro’s, die ook last hebben van de “dubbele evaluatie” moeilijkheid, maar ze zijn er voor degenen die hun eigen niet opnieuw willen rollen 🙂


Antwoord 11

Ik weet dat de man “C” zei…
Maar als je de kans hebt, gebruik dan een C++-sjabloon:

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

Typ veilig, en geen problemen met de ++ genoemd in andere opmerkingen.


Antwoord 12

Oude GCC-extensie: operators <?, >?, <?=, >?=

In een zeer oude versie van GCC waren er de operators <?, >?(zie hier, hier was het in C++ maar ik denk dat het toen ook als C-extensie werd toegepast)
Ik heb ook de operatoren <?=, >?=gezien die overeenkomen met de toewijzingsinstructies.

De operanden werden één keer geëvalueerd en lieten zelfs een zeer korte opdrachtverklaring toe. Het is erg kort in vergelijking met veelvoorkomende min/max-opdrachten. Er is niets dat dit kan overtreffen.

Dit waren een afkorting voor het volgende:

min(a, b)   ===   a < b ? a : b   ===   a <? b;
max(a, b)   ===   a > b ? a : b   ===   a >? b;
a = min(a, b);   ===   if(b < a) a = b;   ===   a <?= b;
a = max(a, b);   ===   if(b > a) a = b;   ===   a >?= b;

Het vinden van het minimum is heel beknopt:

int find_min(const int* ints, int num_ints)
{
    assert(num_ints > 0);
    int min = ints[0];
    for(int i = 1; i < num_ints; ++i)
        min <?= ints[i];
    return min;
}

Ik hoop dat dit op een dag kan worden teruggebracht naar GCC, omdat ik denk dat deze operators genoemd zijn.


13

Het maximum van twee gehele getallen aen bis (int)(0.5((a+b)+abs(a-b))). Dit kan ook werken met (double)en fabs(a-b)voor doubles (vergelijkbaar voor drijvers)


14

De eenvoudigste manier is om het te definiëren als een wereldwijde functie in een .h-bestand en het bellen wanneer u maar wilt, als uw programma modulair is met veel bestanden. Zo niet, double MIN(a,b){return (a<b?a:b)}is de eenvoudigste manier.

Other episodes