Quando você deve usar macros em vez de funções inline?
Pergunta
Em uma pergunta o que eu pensava que era uma boa resposta foi rejeitada para o uso sugerido de macros
#define radian2degree(a) (a * 57.295779513082)
#define degree2radian(a) (a * 0.017453292519)
em vez de funções inline. Por favor, desculpe a pergunta novato, mas o que é tão mal sobre macros neste caso?
Solução
Há um par de coisas estritamente más sobre macros.
Eles são processamento de texto, e não são escopo. Se você #define foo 1
, então qualquer posterior utilização foo
como um identificador irá falhar. Isso pode levar a erros de compilação ímpares e erros de tempo de execução de encontrar duros.
Eles não levam argumentos no sentido normal. Você pode escrever uma função que vai levar dois valores int
e retornar o máximo, porque os argumentos serão avaliados uma vez e os valores utilizados posteriormente. Você não pode escrever uma macro para fazer isso, porque vai avaliar pelo menos um argumento duas vezes, e falhar com algo como max(x++, --y)
.
Há também armadilhas comuns. É difícil obter várias declarações direito neles, e eles exigem um monte de parênteses possivelmente supérfluas.
No seu caso, você precisa de parênteses:
#define radian2degree(a) (a * 57.295779513082)
precisa ser
#define radian2degree(a) ((a) * 57.295779513082)
e você ainda está pisando em alguém que escreve uma função radian2degree
em algum espaço interior, confiante de que essa definição vai trabalhar em seu próprio âmbito.
Outras dicas
A maioria das outras respostas discutir por macros são maus incluindo a forma como o seu exemplo tem um uso macro falha comum. Está aqui a tomada de Stroustrup: http://www.research.att.com/~bs /bs_faq2.html#macro
Mas a sua pergunta estava perguntando o que macros ainda são bons para. Há algumas coisas onde as macros são melhores do que funções inline, e é aí que você está fazendo coisas que simplesmente não pode ser feito com funções embutidas, tais como:
- símbolo colar
- lidar com números de linha ou tal (como para a criação de mensagens de erro no
assert()
) - lidar com coisas que não são expressões (por exemplo, como muitas implementações de uso
offsetof()
usando um nome de tipo para criar uma operação de elenco) - a macro para obter uma contagem de elementos da matriz (não pode fazê-lo com uma função, como os decaimentos nome da matriz para um ponteiro com muita facilidade)
- função semelhante a criação de 'tipo polimórfico' coisas em C, onde modelos não estão disponíveis
Mas com uma linguagem que tem em linha funções, os usos mais comuns de macros não deve ser necessário. Eu sou mesmo relutantes em usar macros quando estou lidando com um compilador C que não suporta funções inline. E eu não tentar usá-los para criar funções do tipo agnóstico, se possível (criando várias funções com um indicador de tipo como parte do nome do lugar).
Eu também mudou-se para usar enums para constantes numéricas nomeados em vez de #define
.
Para esta macro específica, se eu usá-lo da seguinte forma:
int x=1;
x = radian2degree(x);
float y=1;
y = radian2degree(y);
não haveria nenhuma verificação de tipo, e x, y conterá valores diferentes.
Além disso, o código a seguir
float x=1, y=2;
float z = radian2degree(x+y);
não vai fazer o que você pensa, uma vez que irá traduzir para
float z = x+y*0.017453292519;
em vez de
float z = (x+y)+0.017453292519;
que é o resultado esperado.
Estes são apenas alguns exemplos para o mau comportamento ans uso indevido macros possa ter.
Editar
Você pode ver adicionais discussões sobre este aqui
As macros são relativamente freqüentemente abusada e pode-se facilmente cometer erros usando-os como demonstrado pelo seu exemplo. Leve o radian2degree expressão (1 + 1):
- com a macro se expandirá para 1 + 1 * 57,29 ... = 58,29 ...
- com uma função que vai ser o que você quer que seja, a saber (1 + 1) * 57,29 ... = ...
De modo mais geral, as macros são maus porque eles olhar como funções para que induzi-lo a usá-los apenas como funções mas eles têm regras sutis de seu próprio . Neste caso, a maneira correta seria escrever seria (aviso o parêntese em torno de um):
#define radian2degree(a) ((a) * 57.295779513082)
Mas você deve manter em linha funções. Veja estas ligações a partir do C ++ FAQ Lite para mais exemplos de macros do mal e suas sutilezas:
Se possível, use sempre função inline. Estes são typesafe e não pode ser facilmente redefinida.
define podem ser redfined indefinido, e não há nenhuma verificação de tipo.
pré-processador do compilador é uma coisa finnicky, e, portanto, um candidato terrível para truques inteligentes. Como outros têm para fora pontas, é fácil para o compilador para entender mal sua intenção com a macro, e é fácil para você não entender o que a macro vai realmente fazer, mas o mais importante, você não pode entrar em macros no depurador
Macros são maus, porque você pode acabar passando mais de uma variável ou um escalar a ele e isso pode resolver em um comportamento indesejado (definir uma macro máximo para determinar máximo entre um e b, mas passar um ++ e b ++ para a macro e ver o que acontece).
Se a sua função vai ser embutido de qualquer maneira, não há nenhuma diferença de desempenho entre uma função e uma macro. No entanto, existem várias diferenças de usabilidade entre uma função e uma macro, os quais defendem o uso de uma função.
Se você construir a macro corretamente, não há nenhum problema. Mas se você usar uma função, o compilador irá fazê-lo corretamente para você o tempo todo. Então, usando uma função faz com que seja mais difícil de escrever código ruim.