"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Funções embutidas em C e C++

Funções embutidas em C e C++

Publicado em 2024-11-08
Navegar:792

Inline Functions in C and C

Introdução

C adicionou a palavra-chave inline que pode prefixar uma definição de função, como:

inline int max_int( int a, int b ) {
    return a > b ? a : b;
}

para dar ao compilador uma “dica” de que o programa em geral pode se beneficiar no desempenho da função que está sendo inlined.

Uma função que foi inlined teve seu código expandido em cada ponto em que foi chamada, em vez de executar o mecanismo normal de chamada de função de:

  • Salvando registros.
  • Empurrando valores de argumentos para a pilha.
  • Executando a instrução de chamada.
  • A função eventualmente executando a instrução ret.
  • Restaurando registros.

Para funções muito pequenas, o inlining pode gerar um ganho de desempenho. Mas, como quase tudo, existem compensações.

A palavra-chave inline foi portada para C99, mas com requisitos ligeiramente diferentes – falaremos mais tarde.

Diferenças de macros

As funções inline são como (e pretendem substituir muitos usos de) macros semelhantes a funções. Geralmente, isso é bom porque funções inline são funções e têm semântica de função completa em vez de mera substituição de texto feita pelo pré-processador que não entende C ou C .

Uma macro ingenuamente equivalente à função max_int():

#define MAX_INT(A,B)  A > B ? A : B  /* bad implementation */

tem os seguintes problemas:

  • Argumentos expandidos, por exemplo, MAX(n & 0xFF, 8), podem resultar na precedência do operador errada.
  • Argumentos com efeitos colaterais, por exemplo, MAX(n , 8), podem ter vários efeitos colaterais.
  • Não há verificação de tipo dos argumentos na definição.
  • Os erros costumam ser detalhados e difíceis de ler.

Além disso, uma macro:

  • Pode modificar seu argumento (isso geralmente não é o que você deseja).

As funções inline não têm nenhum desses problemas, mas podem gerar o mesmo benefício de desempenho. Portanto, use funções embutidas em vez de macros semelhantes a funções.

Apenas uma dica

Como mencionado, especificar inline é apenas uma “dica” para o compilador de que o programa como um todo pode se beneficiar no desempenho da função que está sendo inline. O compilador é livre para ignorar a dica.

Por que? Porque há casos em que não é uma boa ideia ou é impossível. Uma função não está embutida ou normalmente não está embutida quando qualquer uma das seguintes opções é verdadeira:

  • A função é “muito grande”.
  • Você chama a função por meio de um ponteiro para função.
  • A função é recursiva.
  • A função tem um loop.

Pode haver outros motivos. Tudo depende muito da função, de seus argumentos, do compilador e de quaisquer opções fornecidas a ela.

Se o compilador não puder ou optar por não incorporar uma função, ele não avisa que não fez isso (por padrão). Alguns compiladores, por exemplo, gcc, têm uma opção -Winline que irá avisá-lo e fornecer o motivo pelo qual uma função não foi incorporada.

A especificação inline é semelhante ao código antigo que especifica o registro - ambos são apenas dicas.

Quando (e quando não) incorporar

Para a maioria das funções, a maior parte do custo de execução da função está no corpo da função, não no mecanismo de chamada de função. Portanto, para que uma função seja uma boa candidata para inlining, ela geralmente deve ser:

  • Pequeno o suficiente para que o custo do mecanismo de chamada de função domine.
  • Usado em locais onde o desempenho realmente importa, por exemplo, em loops apertados.

Em caso de dúvida, crie um perfil do seu código. Usar inline não é uma palavra-chave mágica do tipo “me deixe mais rápido”. Além disso, o uso excessivo de inline pode levar ao inchaço do código, o que também torna o desempenho do seu programa pior em geral.

Para mais informações, consulte A doença inline.

Funções que geralmente são boas candidatas para inlining incluem:

  • “One-liners”, como “getters” e “setters”.
  • Invólucros simples em torno de chamadas para outras funções que fornecem valores específicos para argumentos ou fazem conversões.

Uma função inline ideal ambos aumenta o desempenho e diminui o tamanho do código.

No entanto, uma ressalva para qualquer função inline é que se sua definição mudar, será necessário recompilar todo o código que a utiliza.

Otimizações Inline

Se uma função embutida for realmente embutida pelo compilador, então, além de eliminar o código do mecanismo normal de chamada de função, o compilador também poderá:

  • Elimine completamente um ou mais argumentos de função cujos valores sejam constantes por meio de endereçamento imediato.
  • Execute otimizações melhores abrangendo o código no qual a função está incorporada e que normalmente não poderia ser executada além dos limites da função.

Definição de função embutida

Para que o compilador seja capaz de incorporar uma função, ele deve ser capaz de “ver” sua definição (não apenas sua declaração) em cada arquivo .c ou .cpp em que é usado assim como uma macro. Portanto, uma função embutida deve ser definida em um arquivo de cabeçalho.

Normalmente, uma função, como todo o resto, deve ter exatamente uma definição aderindo à regra de uma definição (ODR). No entanto, como a definição de uma função embutida é “vista” em vários arquivos .c ou .cpp, o ODR é suspenso para essa função.

É possível ter definições diferentes para funções inline com o mesmo nome, mas isso resulta em comportamento indefinido, pois o compilador não tem como verificar se todas as definições são iguais.

Para incorporar uma função em C , tudo que você precisa fazer é prefixar a definição da função com inline - é isso. O compilador e/ou vinculador descartará automaticamente todas as definições, exceto uma, do arquivo executável final para você.

No entanto, para incorporar uma função em C, você adicionalmente deve informar explicitamente ao compilador em qual arquivo .o colocar a definição no caso de o compilador não poder ou não querer incorporar uma função via embutido externo.

Por exemplo, em exatamente um arquivo .c, você declararia uma função como:

// util.c
extern inline int max_int( int, int );

Isso diz ao compilador para “colocar a única definição para max_int() em util.o.”

Alternativamente em C, você também pode declarar uma função embutida estática:

static inline int max_int( int a, int b ) {
    return a > b ? a : b;
}

Se você fizer isso, então:

  • Você não precisa declarar uma função extern inline em qualquer lugar.
  • No entanto, se o compilador não incorporar uma função, ele gerará uma definição em cada arquivo .c em que for incluído novamente, levando ao inchaço do código.
  • Se a função tiver alguma variável local estática, cada definição terá cópias distintas (que podem ou não ser o que você deseja).

Conclusão

Funções inline, se usadas criteriosamente, podem gerar ganhos de desempenho. Geralmente, apenas funções muito pequenas são boas candidatas para inlining.

A partir de C 11, funções inline podem alternativamente ser declaradas constexpr, mas isso é uma história para outro momento.

Referências

  • Estilo de codificação do kernel Linux, §15 A doença inline.
  • Mito e realidade sobre inline em C99.
  • O Manual de Referência C Anotado, Margaret A. Ellis & Bjarne Stroustrup, Addison-Wesley, 1990, ISBN 0-201-51459-1, §7.1.2 Especificadores de Função, pp. 99–105.
Declaração de lançamento Este artigo está reproduzido em: https://dev.to/pauljlucas/inline-functions-in-c-and-c-2040 Se houver alguma infração, entre em contato com [email protected] para excluí-la
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3