MIN e MAX in C

Dove sono MIN e MAX definiti in C, se non del tutto?

Qual è il modo migliore per implementarli, in modo generico e digitare il più sicuro ansible? (Le estensioni del compilatore / builtin per i compilatori mainstream preferiti.)

Dove sono MIN e MAX definiti in C, se non del tutto?

Non lo sono.

Qual è il modo migliore per implementarli, in modo generico e sicuro come il più ansible (estensioni compilatore / builtin per i compilatori mainstream preferiti).

Come funzioni Non #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) macro come #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) , soprattutto se prevedi di distribuire il tuo codice. Scrivi il tuo, usa qualcosa come fmax o fmin standard, o correggi la macro usando il tipo di GCC (ottieni anche il bonus di typesafety):

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

Tutti dicono "oh, so di una doppia valutazione, non c'è problema" e pochi mesi dopo, per ore e ore, farai il debug dei problemi più stupidi.

Nota l'uso di __typeof__ invece di typeof :

Se stai scrivendo un file di intestazione che deve funzionare quando incluso nei programmi ISO C, scrivi __typeof__ invece di typeof .

È fornito anche nelle versioni GNU libc (Linux) e FreeBSD di sys / param.h e ha la definizione fornita da dreamlax.


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

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

I repository di origine sono qui:

  • GNU C Library
  • FreeBSD

C’è uno std::min e std::max in C ++, ma AFAIK, non c’è equivalente nella libreria standard C. Puoi definirli tu stesso con macro come

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

Ma questo causa problemi se scrivi qualcosa come MAX(++a, ++b) .

Evita le estensioni del compilatore non standard e implementala come una macro completamente sicura per il tipo in puro standard C (ISO 9899: 2011).

Soluzione

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

uso

 MAX(int, 2, 3) 

Spiegazione

La macro MAX crea un’altra macro in base al parametro type . Questa macro di controllo, se implementata per il tipo specificato, viene utilizzata per verificare che entrambi i parametri siano del tipo corretto. Se il type non è supportato, si verificherà un errore del compilatore.

Se x o y non è del tipo corretto, ci sarà un errore del compilatore nelle macro ENSURE_ . Più macro di questo tipo possono essere aggiunte se sono supportati più tipi. Ho assunto che verranno utilizzati solo i tipi aritmetici (interi, float, puntatori, ecc.) E non le strutture o gli array ecc.

Se tutti i tipi sono corretti, verrà chiamata la macro GENERIC_MAX. Sono necessarie parentesi extra attorno a ciascun parametro macro, come la normale precauzione standard quando si scrivono macro C.

Poi ci sono i soliti problemi con promozioni di tipo implicito in C. L’operatore ?: Bilancia il 2 ° e il 3 ° operando l’uno contro l’altro. Ad esempio, il risultato di GENERIC_MAX(my_char1, my_char2) sarebbe un int . Per evitare che la macro effettui promozioni di tipo potenzialmente pericolose, è stato utilizzato un cast di tipo finale per il tipo desiderato.

Fondamento logico

Vogliamo che entrambi i parametri della macro siano dello stesso tipo. Se uno di questi è di tipo diverso, la macro non è più sicura, perché un operatore come ?: Produrrà promozioni di tipo implicite. E poiché lo fa, abbiamo anche sempre bisogno di restituire il risultato finale al tipo previsto, come spiegato sopra.

Una macro con un solo parametro avrebbe potuto essere scritta in un modo molto più semplice. Ma con 2 o più parametri, è necessario includere un parametro di tipo extra. Perché qualcosa del genere è purtroppo imansible:

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

Il problema è che se la macro precedente viene chiamata come MAX(1, 2) con due int , continuerà comunque a espandere in macro tutti gli scenari possibili della lista di associazioni _Generic . Quindi anche la macro ENSURE_float verrà espansa, anche se non è rilevante per int . E poiché quella macro contiene intenzionalmente solo il tipo float , il codice non verrà compilato.

Per risolvere questo, ho creato il nome della macro durante la fase di pre-processore, invece, con l’operatore ##, in modo che nessuna macro venga accidentalmente espansa.

Esempi

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

Non penso che siano macro standardizzate. Esistono già funzioni standardizzate per floating point, fmax e fmin (e fmaxf per float e fmaxl per long double).

Puoi implementarli come macro se sei a conoscenza dei problemi degli effetti collaterali / doppia valutazione.

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

Nella maggior parte dei casi, puoi lasciarlo al compilatore per determinare cosa stai cercando di fare e ottimizzarlo al meglio. Mentre questo causa problemi quando usato come MAX(i++, j++) , dubito che ci sia sempre molto bisogno nel controllare il massimo dei valori incrementati in un colpo solo. Incrementa prima, quindi controlla.

Questa è una risposta tardiva, a causa di uno sviluppo abbastanza recente. Dal momento che l’OP ha accettato la risposta che si basa su un tipo di estensione GCC (e clang) non portabile – o __typeof__ per ISO C “pulito” – è disponibile una soluzione migliore a partire da gcc-4.9 .

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

L’ovvio vantaggio di questa estensione è che ogni argomento macro viene espanso una sola volta, a differenza della soluzione __typeof__ .

__auto_type è una forma limitata di auto di C ++ 11. Non può (o non dovrebbe?) Essere usato nel codice C ++, sebbene non ci sia una buona ragione per non usare le capacità di inferenza di tipo superiore di auto quando si usa C ++ 11.

Detto questo, presumo che non ci siano problemi nell’usare questa syntax quando la macro è inclusa in un ambito extern "C" { ... } ; ad esempio, da un’intestazione C. AFAIK, questa estensione non ha trovato il suo modo di info clang

Ho scritto questa versione che funziona per MSVC, GCC, C e C ++.

 #if defined(__cplusplus) && !defined(__GNUC__) # include  # define MIN std::min # define MAX std::max //# define TMIN(T, a, b) std::min(a, b) //# define TMAX(T, a, b) std::max(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 

Se hai bisogno di min / max per evitare un ramo costoso, non dovresti usare l’operatore ternario, poiché si compilerà fino a un salto. Il collegamento seguente descrive un metodo utile per implementare una funzione min / max senza ramificazione.

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

So che il ragazzo ha detto “C” … Ma se ne hai la possibilità, usa un modello C ++:

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

Digitare sicuro e nessun problema con ++ menzionato in altri commenti.

Vale la pena sottolineare che penso che se si definisce min e max con il terziario come

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

quindi per ottenere lo stesso risultato per il caso speciale di fmin(-0.0,0.0) e fmax(-0.0,0.0) è necessario scambiare gli argomenti

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

Sembra che Windef.h (a la #include ) abbia macro min e min (minuscole), che soffrono anche della difficoltà di “doppia valutazione”, ma sono lì per quelli che non vogliono ri-roll proprio 🙂

Il massimo di due interi a e b è (int)(0.5((a+b)+abs(ab))) . Questo può anche funzionare con (double) e fabs(ab) per i doppi (simile per i galleggianti)

Il modo più semplice è definirlo come una funzione globale in un file .h , e chiamarlo quando vuoi, se il tuo programma è modulare con molti file. In caso contrario, il double MIN(a,b){return (a è il modo più semplice.