Onde estão MINe MAXdefinidos em C, se houver?
Qual é a melhor maneira de implementá-las da maneira mais genérica e segura possível? (Preferências de extensões / built-in do compilador para compiladores convencionais).
Onde estão MINe MAXdefinidos em C, se houver?
Qual é a melhor maneira de implementá-las da maneira mais genérica e segura possível? (Preferências de extensões / built-in do compilador para compiladores convencionais).
Respostas:
Onde estão
MINeMAXdefinidos em C, se houver?
Eles não são.
Qual é a melhor maneira de implementá-las, da maneira mais genérica e segura possível (extensões / compiladores do compilador para compiladores principais 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 você mesmo, use algo como padrão fmaxou fmincorrija a macro usando o typeof do GCC (você também recebe bônus de segurança de tipo) em uma expressão de instrução do GCC :
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
Todo mundo diz "ah, 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 em __typeof__vez de typeof:
Se você estiver escrevendo um arquivo de cabeçalho que deve funcionar quando incluído nos programas ISO C, escreva em
__typeof__vez detypeof.
decltypepalavra-chave do MSVC ++ 2010 - mas, mesmo assim, o Visual Studio não pode fazer declarações compostas em macros (e decltypeé C ++ de qualquer maneira), ou seja, ({ ... })sintaxe do GCC, então eu tenho certeza que não é possível. Eu não olhei para quaisquer outros compiladores quanto a esta questão, desculpe Luther: S
MAX(someUpperBound, someRandomFunction())para limitar um valor aleatório a algum limite superior. Foi uma péssima ideia, mas também nem funcionou, porque o que MAXele estava usando tinha o problema de dupla avaliação, então ele acabou com um número aleatório diferente daquele que foi avaliado inicialmente.
MIN(x++, y++)o pré-processador, irá gerar o seguinte código (((x++) < (y++)) ? (x++) : (y++)). Então, xe yserá incrementado duas vezes.
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.
No 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:
openSUSE/Linux 3.1.0-1.2-desktop/ gcc version 4.6.2 (SUSE Linux) também. :) Ruim, não é portátil.
Existe um std::mine std::maxno C ++, mas no AFAIK, não há equivalente na biblioteca padrão do C. Você mesmo pode defini-los 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 parecido MAX(++a, ++b).
#define MIN(A, B) ((A < B) ? A : B)não é uma maneira flexível, por que ???
#define MULT(x, y) x * y. Em seguida, MULT(a + b, a + b)expande para a + b * a + b, que analisa como a + (b * a) + bdevido à precedência. Não é isso que o programador provavelmente pretendeu.
Evite extensões de compilador não padrão e implemente-a como uma macro completamente segura para o tipo no padrão C (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 com base no typeparâmetro. Essa macro de controle, se implementada para o tipo especificado, é usada para verificar se os dois parâmetros são do tipo correto. Se o typenão for suportado, haverá um erro do compilador.
Se x ou y não for do tipo correto, haverá um erro do compilador nas ENSURE_macros. Mais macros podem ser adicionadas se mais tipos forem suportados. Eu assumi que apenas tipos aritméticos (números inteiros, flutuadores, 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 de macro, como a precaução padrão usual ao gravar macros C.
Depois, há os problemas usuais com promoções implícitas de tipos em C. 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 impedir que a macro faça essas promoções de tipo potencialmente perigosas, foi usada uma conversão de tipo final para o tipo pretendido.
Fundamentação
Queremos que ambos os parâmetros para a macro sejam do mesmo tipo. Se um deles for de um tipo diferente, a macro não será mais um tipo seguro, porque um operador como esse ?:produzirá promoções implícitas de tipo. E, como isso acontece, também sempre precisamos converter o resultado final no tipo pretendido, conforme explicado acima.
Uma macro com apenas um parâmetro poderia ter sido escrita de uma maneira 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)duas int, ela ainda tentará expandir macro todos os cenários possíveis da _Genericlista de associações. Portanto, a ENSURE_floatmacro também será expandida, mesmo que não seja relevante int. E como essa macro intencionalmente contém apenas o floattipo, 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;
}
GENERIC_MAXpropósito, essa macro é uma péssima idéia; você só precisa tentar GENERIC_MAX(var++, 7)descobrir o motivo :-) Hoje em dia (especialmente com compiladores altamente otimizados / embutidos), as macros devem ser praticamente relegadas apenas aos formulários simples. Os do tipo função são melhores como funções e os do grupo de valor melhores como enumerações.
Não acho que sejam macros padronizadas. Já existem funções padronizadas para ponto flutuante fmaxe fmin(e fmaxfpara flutuadores e fmaxlpara dobras longas).
Você pode implementá-las 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 para o compilador determinar o que está tentando fazer e otimizá-lo da melhor forma possível. Embora isso cause problemas quando usado como MAX(i++, j++), duvido que exista muita necessidade de verificar o máximo de valores incrementados de uma só vez. Incremente primeiro e depois verifique.
Esta é uma resposta tardia, devido a um desenvolvimento relativamente recente. Como o OP aceitou a resposta que se baseia em uma extensão GCC (e clang) não portátil typeof- ou __typeof__para ISO C 'limpo' - há uma solução melhor disponível a partir do 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 da __typeof__solução.
__auto_typeé uma forma limitada de C ++ 11 auto. Ele não pode (ou não deveria?) Ser usado no código C ++, embora não haja uma boa razão para não usar os recursos superiores de inferência de tipo autoao usar o C ++ 11.
Dito isso, presumo que não há problemas ao usar essa sintaxe quando a macro é incluída em um extern "C" { ... }escopo; por exemplo, de um cabeçalho C. AFAIK, esta extensão não encontrou seu caminho. Info clang
clangcomeçou a oferecer suporte por __auto_typevolta de 2016 (consulte o patch ).
c-preprocessortag. Não é garantido que uma função seja incorporada, mesmo com a referida palavra-chave, a menos que seja usado algo como o __always_inline__atributo do gcc .
Eu escrevi esta versão que 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 ramificação cara, não use o operador ternário, pois ele será compilado 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#IntegerMinOrMax
@ David Titarenco pregado-lo aqui , mas deixe-me pelo menos limpá-lo um pouco para torná-lo uma boa aparência, e mostrar tanto min() e max() em conjunto para fazer copiando e colando a partir daqui mais fácil. :)
Atualização 25 de abril de 2020: Eu também adicionei uma Seção 3 para mostrar como isso seria feito com os modelos C ++ também, como uma comparação valiosa para quem aprende C e C ++ ou faz a transição de um para o outro. Esforcei-me ao máximo para ser meticuloso, factual e correto, a fim de fazer desta resposta uma referência canônica para a qual posso voltar várias vezes, e espero que você a ache tão útil quanto eu.
Essa técnica é comumente usada, respeitada por quem sabe usá-la adequadamente, a maneira "de fato" de fazer as coisas, e pode ser usada se usada corretamente, mas com erros (pense: efeito colateral de avaliação dupla ) se você sempre passe expressões incluindo atribuição de variáveis para comparar:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
Essa técnica evita os efeitos colaterais e bugs da "dupla avaliação" acima e, portanto, é considerada o CCG superior, mais seguro e "mais moderno" maneira C do C para fazer isso. Espere que ele funcione com os compiladores gcc e clang, pois o clang é, por design, compatível com o gcc (consulte a nota do clang na parte inferior desta resposta).
MAS: Cuidado com os efeitos de " sombreamento variável " ainda, pois as expressões de instrução são aparentemente embutidas e, portanto, NÃO têm seu próprio escopo de variável local!
#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; \
})
Observe que nas expressões de instrução gcc, a última expressão no bloco de código é o que é "retornado" da expressão, como se tivesse sido retornado de uma função. A documentação do GCC diz o seguinte:
A última coisa na declaração composta deve ser uma expressão seguida de ponto e vírgula; o valor dessa subexpressão serve como o valor de toda a construção. (Se você usar algum outro tipo de instrução por último entre chaves, a construção terá o tipo nulo e, portanto, efetivamente sem valor.)
C ++ Nota: se estiver usando C ++, provavelmente os modelos são recomendados para esse tipo de construção, mas eu pessoalmente não gosto de modelos e provavelmente usaria uma das construções acima em C ++ de qualquer maneira, pois frequentemente uso e prefiro estilos C em C ++ incorporado.
Esta seção foi adicionada em 25 de abril de 2020:
Venho fazendo uma tonelada de C ++ nos últimos meses, e a pressão para preferir modelos em vez de macros, quando possível, na comunidade C ++ é bastante forte. Como resultado, estou aprimorando o uso de modelos e quero colocar aqui as versões do modelo C ++ para garantir a integridade e tornar essa resposta mais canônica e completa.
A seguir, quais são as versões básicas do modelo de funçãomax() e que min()podem parecer no C ++:
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;
}
Faça leituras adicionais sobre modelos C ++ aqui: Wikipedia: Template (C ++) .
No entanto, ambos max()e min()já fazem parte da biblioteca padrão C ++, no <algorithm>cabeçalho ( #include <algorithm>). Na biblioteca padrão do C ++, eles são definidos de maneira ligeiramente diferente da que eu os tenho acima. Os protótipos padrão para std::max<>()e std::min<>(), por exemplo, no C ++ 14, observando seus protótipos nos links cplusplus.com logo acima, são:
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);
Observe que a palavra typename- chave é um alias para class(portanto, seu uso é idêntico, independentemente de você dizer <typename T>ou não <class T>), já que mais tarde foi reconhecido após a invenção dos modelos C ++, que o tipo de modelo pode ser um tipo regular ( int,float , etc.) em vez de apenas um tipo de classe.
Aqui você pode ver que ambos os tipos de entrada, assim como o tipo de retorno, são const T&, o que significa "referência constante ao tipo T". Isso significa que os parâmetros de entrada e o valor de retorno são passados por referência em vez de passados por valor . É como passar por ponteiros e é mais eficiente para tipos grandes, como objetos de classe. A constexprparte da função modifica a própria função e indica que a função deve ser capaz de ser avaliada em tempo de compilação (pelo menos se houver constexprparâmetros de entrada fornecidos ), mas se não puder ser avaliada em tempo de compilação, o padrão será retornado para um avaliação em tempo de execução, como qualquer outra função normal.
O aspecto em tempo de compilação de uma constexprfunção C ++ o torna semelhante a macro-C, pois se a avaliação em tempo de compilação for possível para uma constexprfunção, ela será feita em tempo de compilação, o mesmo que um MIN()ouMAX() substituição de macro poderia possivelmente ser totalmente avaliado em tempo de compilação em C ou C ++ também. Para referências adicionais para essas informações de modelo C ++, consulte abaixo.
Nota da Wikipedia :
O [Clang] foi projetado para atuar como um substituto para o GNU Compiler Collection (GCC), suportando a maioria de seus sinalizadores de compilação e extensões de idioma não oficiais.
Vale ressaltar que acho que se você definir mine maxcom o terciário como
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
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)
fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
Parece que Windef.h(a la #include <windows.h>) possui maxe min(minúsculas) macros, que também sofrem com a dificuldade de "avaliação dupla", mas estão lá para aqueles que não querem relançar 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 safe e sem problemas com o ++ mencionado em outros comentários.
O máximo de dois números inteiros ae bé (int)(0.5((a+b)+abs(a-b))). Isso também pode funcionar com (double)e fabs(a-b)para duplos (semelhante para carros alegóricos)
A maneira mais simples é defini-la como uma função global em um .harquivo e chamá-la sempre que quiser, se o seu programa for modular com muitos arquivos. Caso contrário, double MIN(a,b){return (a<b?a:b)}é a maneira mais simples.
warning: expression with side-effects multiply evaluated by macrono ponto de uso ...