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
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).
Respostas:
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
.
decltype
palavra-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 MAX
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
e y
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:
$ 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::min
e std::max
no 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) + 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
#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 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 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 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 _Generic
lista de associações. Portanto, a ENSURE_float
macro também será expandida, mesmo que não seja relevante int
. E como essa macro intencionalmente contém 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;
}
GENERIC_MAX
propó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 fmax
e fmin
(e fmaxf
para flutuadores e fmaxl
para 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 auto
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 clang
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 ++.
#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 constexpr
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 houver constexpr
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 uma constexpr
funçã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 min
e max
com 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 max
e 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 a
e 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 .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.
warning: expression with side-effects multiply evaluated by macro
no ponto de uso ...