Onde estão MIN
e MAX
definidos 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).
fonte
Onde estão MIN
e MAX
definidos 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
MIN
eMAX
definidos 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 fmax
ou fmin
corrija 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
.
warning: expression with side-effects multiply evaluated by macro
no ponto de uso ...decltype
palavra-chave do MSVC ++ 2010 - mas, mesmo assim, o Visual Studio não pode fazer declarações compostas em macros (edecltype
é 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: SMAX(someUpperBound, someRandomFunction())
para limitar um valor aleatório a algum limite superior. Foi uma péssima ideia, mas também nem funcionou, porque o queMAX
ele 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,x
ey
será 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:
No FreeBSD:
Os repositórios de origem estão aqui:
fonte
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::min
estd::max
no C ++, mas no AFAIK, não há equivalente na biblioteca padrão do C. Você mesmo pode defini-los com macros comoMas isso causa problemas se você escrever algo parecido
MAX(++a, ++b)
.fonte
#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 paraa + b * a + b
, que analisa comoa + (b * a) + b
devido à 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
Uso
Explicação
A macro MAX cria outra macro com base no
type
parâ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 otype
nã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 deGENERIC_MAX(my_char1, my_char2)
seria umint
. 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:
O problema é que, se a macro acima for chamada como
MAX(1, 2)
duasint
, ela ainda tentará expandir macro todos os cenários possíveis da_Generic
lista de associações. Portanto, aENSURE_float
macro também será expandida, mesmo que não seja relevanteint
. E como essa macro intencionalmente contém apenas ofloat
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
fonte
GENERIC_MAX
propósito, essa macro é uma péssima idéia; você só precisa tentarGENERIC_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
fmax
efmin
(efmaxf
para flutuadores efmaxl
para dobras longas).Você pode implementá-las como macros, desde que esteja ciente dos problemas de efeitos colaterais / avaliação dupla.
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.fonte
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 .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 ++ 11auto
. 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 tipoauto
ao 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 clangfonte
clang
começou a oferecer suporte por__auto_type
volta de 2016 (consulte o patch ).c-preprocessor
tag. 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 ++.
fonte
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
fonte
@ 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()
emax()
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.
1. A velha maneira macro C:
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:
2. A nova e aprimorada maneira de " expressão de declaração " do gcc :
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!
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:
3. A maneira do modelo C ++:
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ção
max()
e quemin()
podem parecer no C ++:Faça leituras adicionais sobre modelos C ++ aqui: Wikipedia: Template (C ++) .
No entanto, ambos
max()
emin()
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 parastd::max<>()
estd::min<>()
, por exemplo, no C ++ 14, observando seus protótipos nos links cplusplus.com logo acima, são:Observe que a palavra
typename
- chave é um alias paraclass
(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 tipoT
". 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. Aconstexpr
parte 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 houverconstexpr
parâ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
constexpr
função C ++ o torna semelhante a macro-C, pois se a avaliação em tempo de compilação for possível para umaconstexpr
função, ela será feita em tempo de compilação, o mesmo que umMIN()
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.Referências:
Nota da Wikipedia :
fonte
Vale ressaltar que acho que se você definir
min
emax
com o terciário comopara obter o mesmo resultado para o caso especial de
fmin(-0.0,0.0)
efmax(-0.0,0.0)
você precisa trocar os argumentosfonte
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>
) possuimax
emin
(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 :)fonte
Eu sei que o cara disse "C" ... Mas se você tiver a chance, use um modelo C ++:
Digite safe e sem problemas com o ++ mencionado em outros comentários.
fonte
O máximo de dois números inteiros
a
eb
é(int)(0.5((a+b)+abs(a-b)))
. Isso também pode funcionar com(double)
efabs(a-b)
para duplos (semelhante para carros alegóricos)fonte
A maneira mais simples é defini-la como uma função global em um
.h
arquivo 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.fonte