Sempre vi exemplos e casos em que usar macro é melhor do que usar função.
Alguém poderia me explicar com um exemplo a desvantagem de uma macro em relação a uma função?
c
function
c-preprocessor
Kyrol
fonte
fonte
Respostas:
As macros estão sujeitas a erros porque dependem da substituição textual e não executam a verificação de tipo. Por exemplo, esta macro:
funciona bem quando usado com um inteiro:
mas faz coisas muito estranhas quando usado com expressões:
Colocar os argumentos entre parênteses ajuda, mas não elimina completamente esses problemas.
Quando as macros contêm várias instruções, você pode ter problemas com construções de fluxo de controle:
A estratégia usual para consertar isso é colocar as instruções dentro de um loop "do {...} while (0)".
Se você tiver duas estruturas que contenham um campo com o mesmo nome, mas semânticas diferentes, a mesma macro pode funcionar em ambos, com resultados estranhos:
Finalmente, macros podem ser difíceis de depurar, produzindo erros de sintaxe estranhos ou erros de tempo de execução que você precisa expandir para entender (por exemplo, com gcc -E), porque os depuradores não podem percorrer as macros, como neste exemplo:
As funções e constantes embutidas ajudam a evitar muitos desses problemas com macros, mas nem sempre são aplicáveis. Quando as macros são usadas deliberadamente para especificar o comportamento polimórfico, o polimorfismo não intencional pode ser difícil de evitar. C ++ tem uma série de recursos, como modelos para ajudar a criar construções polimórficas complexas de uma maneira segura de tipos, sem o uso de macros; consulte The C ++ Programming Language de Stroustrup para obter detalhes.
fonte
x++*x++
não se pode dizer que a expressão aumentax
duas vezes; na verdade, ele invoca um comportamento indefinido , o que significa que o compilador está livre para fazer o que quiser - ele pode incrementarx
duas vezes, ou uma vez, ou não aumentar ; ele pode abortar com um erro ou até mesmo fazer demônios voarem pelo seu nariz .Recursos macro :
Recursos da função :
fonte
Os efeitos colaterais são importantes. Aqui está um caso típico:
é expandido para:
x
é incrementado duas vezes na mesma instrução. (e comportamento indefinido)Escrever macros multilinhas também é uma dor:
Eles exigem um
\
no final de cada linha.As macros não podem "retornar" nada, a menos que você faça uma única expressão:
Não posso fazer isso em uma macro a menos que você use a instrução de expressão do GCC. (EDITAR: Você pode usar um operador vírgula, embora ... esquecido ... Mas ainda pode ser menos legível.)
Ordem de operações: (cortesia de @ouah)
é expandido para:
Mas
&
tem precedência menor do que<
. Portanto,0xFF < 42
é avaliado primeiro.fonte
min(a & 0xFF, 42)
Exemplo 1:
enquanto que:
Exemplo 2:
Comparado com:
fonte
Em caso de dúvida, use funções (ou funções embutidas).
No entanto, as respostas aqui explicam principalmente os problemas com macros, em vez de ter uma visão simples de que macros são ruins porque acidentes bobos são possíveis.
Você pode estar ciente das armadilhas e aprender a evitá-las. Em seguida, use macros apenas quando houver um bom motivo para isso.
Existem certos casos excepcionais em que há vantagens em usar macros, incluindo:
va_args
.por exemplo: https://stackoverflow.com/a/24837037/432509 .
(
__FILE__
,__LINE__
,__func__
). verifique se há condições pré / pós,assert
em caso de falha, ou mesmo declarações estáticas para que o código não compile em uso impróprio (principalmente útil para compilações de depuração).struct
membros estão presentes antes da conversão(pode ser útil para tipos polimórficos) .
Ou verifique se uma matriz atende a alguma condição de comprimento.
consulte: https://stackoverflow.com/a/29926435/432509
func(FOO, "FOO");
, você pode definir uma macro que expande a string para vocêfunc_wrapper(FOO);
(atribuições a múltiplas variáveis, para operações por pixel, é um exemplo de que você pode preferir uma macro em vez de uma função ... embora ainda dependa muito do contexto, já que
inline
funções podem ser uma opção) .Admitidamente, alguns deles dependem de extensões de compilador que não são o padrão C. Significa que você pode acabar com código menos portável, ou ter que
ifdef
usá - los, então eles só serão aproveitados quando o compilador suportar.Evitando instanciação de múltiplos argumentos
Observando isso, já que é uma das causas mais comuns de erros em macros (passagem,
x++
por exemplo, onde uma macro pode ser incrementada várias vezes) .é possível escrever macros que evitam efeitos colaterais com múltiplas instanciações de argumentos.
C11 Genérico
Se você gosta de
square
macros que funcionem com vários tipos e suporte C11, você pode fazer isso ...Expressões de declaração
Esta é uma extensão do compilador suportada por GCC, Clang, EKOPath e Intel C ++ (mas não MSVC) ;
Portanto, a desvantagem das macros é que você precisa saber como usá-las para começar, e que elas não têm suporte tão amplo.
Um benefício é, neste caso, você pode usar a mesma
square
função para muitos tipos diferentes.fonte
Nenhuma verificação de tipo de parâmetros e código é repetida, o que pode levar ao inchaço do código. A sintaxe da macro também pode levar a qualquer número de casos extremos estranhos onde ponto-e-vírgula ou ordem de precedência podem atrapalhar. Aqui está um link que demonstra algum mal macro
fonte
uma desvantagem das macros é que os depuradores leem o código-fonte, que não tem macros expandidas, portanto, executar um depurador em uma macro não é necessariamente útil. Desnecessário dizer que você não pode definir um ponto de interrupção dentro de uma macro como faz com as funções.
fonte
As funções fazem a verificação de tipo. Isso oferece uma camada extra de segurança.
fonte
Adicionando a esta resposta ..
As macros são substituídas diretamente no programa pelo pré-processador (já que são basicamente diretivas do pré-processador). Portanto, eles inevitavelmente usam mais espaço de memória do que uma função respectiva. Por outro lado, uma função requer mais tempo para ser chamada e retornar resultados, e essa sobrecarga pode ser evitada usando macros.
Além disso, as macros têm algumas ferramentas especiais que podem ajudar na portabilidade do programa em diferentes plataformas.
As macros não precisam ser atribuídas a um tipo de dados para seus argumentos, em contraste com as funções.
No geral, eles são uma ferramenta útil na programação. E tanto macroinstruções quanto funções podem ser usadas dependendo das circunstâncias.
fonte
Não notei, nas respostas acima, uma vantagem das funções sobre as macros que considero muito importante:
Funções podem ser passadas como argumentos, macros não.
Exemplo concreto: Você deseja escrever uma versão alternativa da função padrão 'strpbrk' que aceitará, em vez de uma lista explícita de caracteres para pesquisar dentro de outra string, uma função (ponteiro para a) que retornará 0 até que um caractere seja descobriu que passa em algum teste (definido pelo usuário). Uma razão pela qual você pode querer fazer isso é para poder explorar outras funções de biblioteca padrão: em vez de fornecer uma string explícita cheia de pontuação, você poderia passar 'ispunct' de ctype.h em vez disso, etc. Se 'ispunct' foi implementado apenas como uma macro, isso não funcionaria.
Existem muitos outros exemplos. Por exemplo, se sua comparação for realizada por macro ao invés de função, você não pode passá-la para 'qsort' de stdlib.h.
Uma situação análoga em Python é 'imprimir' na versão 2 vs. versão 3 (declaração não passável vs. função passável).
fonte
Se você passar a função como um argumento para a macro, ela será avaliada todas as vezes. Por exemplo, se você chamar uma das macros mais populares:
Curtiu isso
functionThatTakeLongTime será avaliado 5 vezes, o que pode diminuir significativamente o desempenho
fonte