Pergunta

Onde estão MIN e MAX definido em C, se for o caso?

Qual é a melhor maneira de implementá-los, da forma mais genérica e digitada com segurança possível?(Extensões/integrações de compilador preferidas para compiladores convencionais.)

Foi útil?

Solução

Onde estão MIN e MAX definido em C, se é que existe?

Eles não são.

Qual é a melhor maneira de implementá -las, o mais genericamente e o tipo seguro possível (extensões/compiladores/compiladores para os principais compiladores preferidos).

Como funções. Eu não usaria macros como #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)), especialmente se você planeja implantar seu código. Escreva o seu próprio, use algo como padrão fmax ou fmin, ou consertar a macro usando Tipo do GCC (você recebe bônus de segurança do tipo):

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

Todo mundo diz: "Oh, eu sei sobre avaliação dupla, não há problema" e, alguns meses depois, você estará depurando os problemas mais bobos por horas a fio.

Observe o uso de __typeof__ ao invés de typeof:

Se você estiver escrevendo um arquivo de cabeçalho que deve funcionar quando incluído nos programas ISO C, escreva __typeof__ ao invés de typeof.

Outras dicas

Também é fornecido nas versões GNU Libc (Linux) e FreeBSD do SYS/Param.H e possui a definição fornecida pelo Dreamlax.


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

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

Os repositórios de origem estão aqui:

Há um std::min e std::max Em C ++, mas Afaik, não há equivalente na biblioteca padrão C. Você pode defini -los você mesmo com macros como

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

Mas isso causa problemas se você escrever algo como MAX(++a, ++b).

Evite extensões de compilador não padrão e implemente-as como uma macro completamente segura de tipo em C padrão puro (ISO 9899:2011).

Solução

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

Explicação

A macro MAX cria outra macro baseada no type parâmetro.Esta macro de controle, se implementada para um determinado tipo, é usada para verificar se ambos os parâmetros são do tipo correto.Se o type não for suportado, haverá um erro do compilador.

Se x ou y não for do tipo correto, haverá um erro do compilador no ENSURE_ macros.Mais dessas macros poderão ser adicionadas se mais tipos forem suportados.Presumi que apenas tipos aritméticos (inteiros, flutuantes, ponteiros, etc.) serão usados ​​e não estruturas ou matrizes, etc.

Se todos os tipos estiverem corretos, a macro GENERIC_MAX será chamada.Parênteses extras são necessários em torno de cada parâmetro da macro, como precaução padrão usual ao escrever macros C.

Depois, há os problemas usuais com promoções implícitas de tipo em C.O ?:O operador equilibra o 2º e o 3º operando entre si.Por exemplo, o resultado de GENERIC_MAX(my_char1, my_char2) seria um int.Para evitar que a macro faça promoções de tipo potencialmente perigosas, foi usado um tipo final convertido para o tipo pretendido.

Justificativa

Queremos que ambos os parâmetros da macro sejam do mesmo tipo.Se um deles for de um tipo diferente, a macro não será mais segura para o tipo, porque um operador como ?: produzirá promoções de tipo implícitas.E porque isso acontece, também precisamos sempre converter o resultado final de volta ao tipo pretendido, conforme explicado acima.

Uma macro com apenas um parâmetro poderia ter sido escrita de uma forma muito mais simples.Mas com 2 ou mais parâmetros, é necessário incluir um parâmetro de tipo extra.Porque algo assim é infelizmente impossível:

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

O problema é que se a macro acima for chamada como MAX(1, 2) com dois int, ainda tentará expandir macro todos os cenários possíveis do _Generic lista de associação.Então o ENSURE_float macro também será expandida, mesmo que não seja relevante para int.E como essa macro contém intencionalmente apenas o float tipo, o código não será compilado.

Para resolver isso, criei o nome da macro durante a fase de pré-processador, com o operador ##, para que nenhuma macro seja expandida acidentalmente.

Exemplos

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

Eu não acho que eles sejam macros padronizados. Já existem funções padronizadas para o ponto flutuante, fmax e fmin (e fmaxf para carros alegóricos e fmaxl por longas duplas).

Você pode implementá-los como macros, desde que esteja ciente dos problemas de efeitos colaterais/avaliação dupla.

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

Na maioria dos casos, você pode deixar o compilador determinar o que está tentando fazer e otimizá -lo da melhor maneira possível. Enquanto isso causa problemas quando usado como MAX(i++, j++), Duvido que haja muita necessidade em verificar o máximo de valores incrementados de uma só vez. Incremento primeiro e depois verifique.

Esta é uma resposta tardia, devido a um desenvolvimento bastante recente. Desde que o OP aceitou a resposta que depende de uma extensão GCC (e Clang) não portável typeof - ou __typeof__ Para ISO "limpo" - há uma solução melhor disponível a partir de GCC-4.9.

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

O benefício óbvio dessa extensão é que cada argumento macro é expandido apenas uma vez, diferentemente do __typeof__ solução.

__auto_type é uma forma limitada de C ++ 11's auto. Não pode (ou não? auto Ao usar C ++ 11.

Dito isto, eu presumir Não há problemas usando esta sintaxe quando a macro é incluída em um extern "C" { ... } alcance; por exemplo, de um cabeçalho C. Afaik, esta extensão não encontrou sua maneira

eu escrevi isto versão Isso funciona para MSVC, GCC, C e 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

Se você precisar de Min/Max para evitar uma filial cara, não deve usar o operador ternário, pois ele se compilará até um salto. O link abaixo descreve um método útil para implementar uma função min/max sem ramificação.

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

Vale a pena apontar, acho que se você definir min e max com o terciário como

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

Então, para obter o mesmo resultado para o caso especial de fmin(-0.0,0.0) e fmax(-0.0,0.0) você precisa trocar os argumentos

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

Parece Windef.h (a la #include <windows.h>) tem max e min (Macros minúsculos), que também sofrem com a dificuldade de "avaliação dupla", mas estão lá para aqueles que não querem re-rolar seus próprios :)

Eu sei que o cara disse "C" ... mas se você tiver a chance, use um modelo C ++:

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

Digite seguro e sem problemas com o ++ mencionado em outros comentários.

O máximo de dois números inteiros a e b é (int)(0.5((a+b)+abs(a-b))). Isso também pode funcionar com (double) e fabs(a-b) Para duplas (semelhantes para carros alegóricos)

Relacionado a Comentário de Brett Hale, clang começou a apoiar __auto_type por volta de 2016 (ver correção).

A maneira mais simples é defini -lo como uma função global em um .h Arquive e ligue para que quiser, se o seu programa é modular com muitos arquivos. Se não, double MIN(a,b){return (a<b?a:b)} é a maneira mais simples.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top