Funções embutidas versus macros de pré-processador

Respostas:

127

Macros de pré-processador são apenas padrões de substituição aplicados ao seu código. Eles podem ser usados ​​em quase qualquer lugar em seu código, pois são substituídos por suas expansões antes do início de qualquer compilação.

Funções inline são funções reais cujo corpo é injetado diretamente em seu site de chamada. Eles só podem ser usados ​​onde uma chamada de função é apropriada.

Agora, no que diz respeito ao uso de macros versus funções inline em um contexto semelhante a uma função, esteja ciente de que:

  • As macros não são seguras quanto ao tipo e podem ser expandidas independentemente de estarem sintaticamente corretas - a fase de compilação relatará erros resultantes de problemas de expansão da macro.
  • As macros podem ser usadas em contextos inesperados, resultando em problemas
  • As macros são mais flexíveis, pois podem expandir outras macros - enquanto as funções inline não necessariamente fazem isso.
  • As macros podem resultar em efeitos colaterais devido à sua expansão, uma vez que as expressões de entrada são copiadas onde quer que apareçam no padrão.
  • Nem sempre é garantido que as funções embutidas sejam sequenciais - alguns compiladores só fazem isso em compilações de lançamento ou quando são especificamente configurados para isso. Além disso, em alguns casos, o inlining pode não ser possível.
  • Funções embutidas podem fornecer escopo para variáveis ​​(particularmente as estáticas), macros de pré-processador só podem fazer isso em blocos de código {...} e variáveis ​​estáticas não se comportarão exatamente da mesma maneira.
LBushkin
fonte
39
Nem sempre é garantido que as funções inline sejam inline: porque o compilador não irá inline, se isso gerar um código mais lento, etc. O compilador faz muitas análises que o engenheiro não consegue e faz a coisa certa.
Martin York
14
Acredito que as funções recursivas são outro exemplo em que a maioria dos compiladores ignora inlining.
LBushkin
Existem diferenças importantes em C em comparação com C ++ neste caso?
rzetterberg
7
Um ponto não mencionado é que o inlining pode ser influenciado por sinalizadores de compilação. Por exemplo, quando você constrói para velocidade máxima (como GCC -O2 / -O3), o compilador escolhe embutir muitas funções, mas quando você constrói para tamanho mínimo (-Os), normalmente funções embutidas são chamadas apenas uma vez (ou funções muito pequenas ) Com macros, não existe essa escolha.
dbrank0
As macros não podem cobrir com especificador de acesso (como, privado ou protegido) enquanto as funções inline são possíveis.
Hit é de
78

Primeiro, as macros do pré-processador são apenas "copiar e colar" no código antes da compilação. Portanto, não há verificação de tipo e alguns efeitos colaterais podem aparecer

Por exemplo, se você deseja comparar 2 valores:

#define max(a,b) ((a<b)?b:a)

Os efeitos colaterais aparecem se você usar max(a++,b++)por exemplo ( aou bserão incrementados duas vezes). Em vez disso, use (por exemplo)

inline int max( int a, int b) { return ((a<b)?b:a); }
ThibThib
fonte
3
Só quero acrescentar ao seu exemplo que, além do efeito colateral, a macro também pode introduzir carga de trabalho extra, considere que max(fibonacci(100), factorial(10000))a maior será calculada duas vezes :(
watashiSHUN
Todo mundo fala sobre verificação de tipo, mas apenas você forneceu um exemplo do mundo real, é por isso que votei positivamente nesta resposta.
Ivanzinho 01/06
16

As funções Inline são expandidas pelo compilador, enquanto as macros são expandidas pelo Pré-processador, que é mera substituição textual.

  • Não há verificação de tipo durante a chamada da macro enquanto a verificação de tipo é feita durante a chamada de função.

  • Resultados indesejados e ineficiências podem ocorrer durante a macroexpansão devido à reavaliação de argumentos e ordem de operações. Por exemplo

    #define MAX(a,b) ((a)>(b) ? (a) : (b))
    int i = 5, j = MAX(i++, 0);

    resultaria em

    int i = 5, j = ((i++)>(0) ? (i++) : (0));
  • Os argumentos macro não são avaliados antes da expansão macro

    #define MUL(a, b) a*b
    int main()
    {
      // The macro is expended as 2 + 3 * 3 + 5, not as 5*8
      printf("%d", MUL(2+3, 3+5));
     return 0;
    }
    // Output: 16`
  • A palavra-chave return não pode ser usada em macros para retornar valores como no caso de funções.

  • Funções inline podem ser sobrecarregadas

  • Os tokens passados ​​para macros podem ser concatenados usando o operador ## chamado operador Token-Pasting.

  • As macros são geralmente usadas para reutilização de código onde, como funções inline, são usadas para eliminar a sobrecarga de tempo (tempo excessivo) durante a chamada de função (evitando um salto para uma sub-rotina).

DataCruncher
fonte
13

A principal diferença é a verificação de tipo. O compilador verificará se o que você passa como valores de entrada é de tipos que podem ser passados ​​para a função. Isso não é verdade com macros de pré-processador - elas são expandidas antes de qualquer verificação de tipo e isso pode causar bugs graves e difíceis de detectar.

Aqui estão vários outros pontos menos óbvios delineados.

dente afiado
fonte
11

Para adicionar outra diferença àquelas já fornecidas: você não pode percorrer a #defineno depurador, mas pode percorrer uma função embutida.

RichieHindle
fonte
8

As macros estão ignorando namespaces. E isso os torna maus.

Kirill V. Lyadvinsky
fonte
3

funções embutidas são semelhantes às macros (porque o código da função é expandido no ponto da chamada no tempo de compilação), funções embutidas são analisadas pelo compilador, enquanto as macros são expandidas pelo pré-processador. Como resultado, existem várias diferenças importantes:

  • As funções inline seguem todos os protocolos de segurança do tipo aplicados nas funções normais.
  • As funções inline são especificadas usando a mesma sintaxe que qualquer outra função, exceto que incluem a palavra-chave inline na declaração da função.
  • As expressões passadas como argumentos para funções embutidas são avaliadas uma vez.
  • Em alguns casos, as expressões passadas como argumentos para macros podem ser avaliadas mais de uma vez. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx

  • as macros são expandidas em tempo de pré-compilação, você não pode usá-las para depuração, mas pode usar funções embutidas.

- bom artigo : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

;

Ahmad
fonte
2

Uma função embutida manterá a semântica de valor, enquanto uma macro de pré-processador apenas copia a sintaxe. Você pode obter erros muito sutis com uma macro de pré-processador se usar o argumento várias vezes - por exemplo, se o argumento contiver mutação como "i ++", ter que executar duas vezes é uma grande surpresa. Uma função embutida não terá esse problema.

Michael Donohue
fonte
1

Uma função embutida se comporta sintaticamente como uma função normal, fornecendo segurança de tipo e um escopo para variáveis ​​locais de função e acesso a membros de classe se for um método. Além disso, ao chamar métodos embutidos, você deve seguir as restrições privadas / protegidas.

heeen
fonte
1

Para saber a diferença entre macro e função inline , em primeiro lugar devemos saber exatamente o que são e quando devemos usá-los.

FUNÇÕES :

int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • As chamadas de função têm sobrecarga associada a ela, pois após o término da execução da função, ela deve saber para onde deve retornar e também armazenar o valor na memória da pilha.

  • Para pequenos aplicativos não será um problema, mas vamos dar um exemplo de aplicativos financeiros onde milhares de transações acontecem a cada segundo, não podemos ir com chamadas de função.

MACROS:

# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • As macros funcionam na fase de pré-processamento, ou seja, nesta fase, as instruções escritas com # palavra-chave serão substituídas pelo conteúdo, ou seja,

resultado interno = quadrado (x * x)

Mas as macros têm bugs associados a ela.

#define Square(x) x*x
int main() {
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Aqui, a saída é 11, não 36 .

FUNÇÕES INLINE :

inline int Square(int x) {
    return x * x;
}

int main() {
    using namespace std;
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Produto 36

A palavra-chave inline solicita que o compilador substitua a chamada de função pelo corpo da função, aqui a saída está correta porque primeiro avalia a expressão e depois é passada. Reduz a sobrecarga da chamada de função, pois não há necessidade de armazenar o endereço de retorno e a pilha a memória não é necessária para argumentos de função.

Comparação entre macros e funções embutidas:

  1. As macros funcionam por meio de substituição, enquanto em funções embutidas, a chamada de função é substituída pelo corpo.
  2. As macros estão sujeitas a erros devido à substituição, enquanto as funções inline são seguras de usar.
  3. As macros não têm endereço, enquanto as funções embutidas têm endereço.
  4. As macros são difíceis de usar com várias linhas de código, enquanto as funções embutidas não.
  5. Em C ++, as macros não podem ser usadas com funções de membro, enquanto as funções embutidas poderiam ser.

CONCLUSÃO:

As funções embutidas às vezes são mais úteis do que as macros, pois melhoram o desempenho e são seguras de usar, além de reduzir a sobrecarga de chamada de função. É apenas uma solicitação ao compilador, certas funções não serão embutidas como:

  • grandes funções
  • funções com muitos argumentos condicionais
  • código recursivo e código com loops etc.

o que é uma coisa boa, porque é sempre que o compilador pensa que é melhor fazer as coisas de outra maneira.

Sheetal
fonte
Apenas como uma observação: A macro pode ser corrigida para avaliar o mesmo número entre colchetes. No entanto, ainda está sujeito a erros, pois você precisa pensar sobre a substituição muda absoluta e todos os casos durante a implementação.
mike
0

No GCC (não tenho certeza sobre os outros), declarar uma função embutida é apenas uma dica para o compilador. Ainda depende do compilador no final do dia decidir se ele inclui ou não o corpo da função sempre que é chamado.

A diferença entre funções in-line e macros do pré-processador é relativamente grande. Macros de pré-processador são apenas substituições de texto no final do dia. Você abre mão de grande parte da capacidade do compilador de executar a verificação de tipo nos argumentos e tipo de retorno. A avaliação dos argumentos é muito diferente (se as expressões que você passar para as funções tiverem efeitos colaterais, você se divertirá muito depurando). Existem diferenças sutis sobre onde as funções e macros podem ser usadas. Por exemplo, se eu tivesse:

#define MACRO_FUNC(X) ...

Onde MACRO_FUNC obviamente define o corpo da função. Deve-se tomar cuidado especial para que funcione corretamente em todos os casos em que uma função possa ser usada, por exemplo, um MACRO_FUNC mal escrito causaria um erro no

if(MACRO_FUNC(y)) {
 ...body
}

Uma função normal poderia ser usada sem nenhum problema.

Falaina
fonte
0

Do ponto de vista da codificação, uma função embutida é como uma função. Portanto, as diferenças entre uma função embutida e uma macro são iguais às diferenças entre uma função e uma macro.

Do ponto de vista da compilação, uma função embutida é semelhante a uma macro. Ele é injetado diretamente no código, não é chamado.

Em geral, você deve considerar as funções embutidas como funções regulares com algumas otimizações menores misturadas. E, como a maioria das otimizações, cabe ao compilador decidir se realmente se importa em aplicá-las. Freqüentemente, o compilador ignora alegremente qualquer tentativa do programador de embutir uma função, por vários motivos.

Brian
fonte
0

as funções embutidas se comportarão como uma chamada de função se houver alguma instrução iterativa ou recursiva nela, de modo a evitar a execução repetida de instruções. É muito útil salvar a memória geral do programa.

Arashdeep singh
fonte
-1
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{ 
    return 10;
}
/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases. 
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */
#define CUBE(b) b*b*b
int cube(int a)
{
 return a*a*a;
}
int main()
{
 cout<<NUMBER<<endl<<number()<<endl;
 cout<<CUBE(1+3); //Unexpected output 10
 cout<<endl<<cube(1+3);// As expected 64
 return 0;
}

As macros são geralmente mais rápidas do que as funções, pois não envolvem sobrecarga de chamada de função real.

Algumas desvantagens das macros: Não há verificação de tipo. É difícil depurar, pois elas causam uma substituição simples. As macros não têm namespace, portanto, uma macro em uma seção do código pode afetar outra seção. As macros podem causar efeitos colaterais, conforme mostrado no exemplo CUBE () acima.

As macros são geralmente um forro. No entanto, eles podem consistir em mais de uma linha. Não existem essas restrições nas funções.

rahul_107
fonte
Quanto mais você se diverte #define TWO_N(n) 2 << ne então cout << CUBE(TWO_N(3 + 1)) << endl;? (É melhor terminar as linhas de saída endldo que começar com elas.)
Jonathan Leffler