Função inline - Inline function
Nas linguagens de programação C e C ++ , uma função embutida é aquela qualificada com a palavra - chave ; isso serve a dois propósitos. Em primeiro lugar, ele serve como uma diretiva do compilador que sugere (mas não exige) que o compilador substitua o corpo da função inline executando a expansão inline , ou seja, inserindo o código da função no endereço de cada chamada de função, economizando assim a sobrecarga de uma chamada de função. Nesse aspecto, é análogo ao especificador de classe de armazenamento , que fornece uma dica de otimização da mesma forma. O segundo objetivo é mudar o comportamento da ligação; os detalhes disso são complicados. Isso é necessário devido ao modelo de compilação + ligação separada C / C ++, especificamente porque a definição (corpo) da função deve ser duplicada em todas as unidades de tradução onde é usada, para permitir inlining durante a compilação , que, se a função tiver ligação , causa uma colisão durante a ligação (viola a exclusividade de símbolos externos). C e C ++ (e dialetos como GNU C e Visual C ++) resolvem isso de maneiras diferentes.
inline
register
inline
Exemplo
Uma inline
função pode ser escrita em C ou C ++ assim:
inline void swap(int *m, int *n)
{
int tmp = *m;
*m = *n;
*n = tmp;
}
Em seguida, uma declaração como a seguinte:
swap(&x, &y);
pode ser traduzido para (se o compilador decidir fazer o inlining, que normalmente requer que a otimização seja ativada):
int tmp = x;
x = y;
y = tmp;
Ao implementar um algoritmo de classificação fazendo muitas trocas, isso pode aumentar a velocidade de execução.
Suporte padrão
C ++ e C99 , mas não seus predecessores K&R C e C89 , têm suporte para inline
funções, embora com semânticas diferentes. Em ambos os casos, inline
não força o inlining; o compilador é livre para escolher não embutir a função de forma alguma, ou apenas em alguns casos. Diferentes compiladores variam em quão complexa é uma função que eles podem gerenciar para embutir. Compiladores C ++ convencionais, como Microsoft Visual C ++ e GCC, oferecem suporte a uma opção que permite aos compiladores embutir automaticamente qualquer função adequada, mesmo aquelas não marcadas como inline
funções. No entanto, simplesmente omitir a inline
palavra-chave para permitir que o compilador tome todas as decisões in-line não é possível, uma vez que o vinculador irá reclamar sobre definições duplicadas em diferentes unidades de tradução. Isso ocorre porque inline
não apenas dá ao compilador uma dica de que a função deve ser embutida, mas também tem um efeito sobre se o compilador irá gerar uma cópia fora de linha que pode ser chamada da função (consulte classes de armazenamento de funções embutidas ).
Extensões fora do padrão
GNU C , como parte do dialeto gnu89 que oferece, tem suporte para inline
como uma extensão para C89. No entanto, a semântica difere de C ++ e C99. armcc no modo C90 também oferece inline
uma extensão não padrão, com semântica diferente de gnu89 e C99.
Algumas implementações fornecem um meio pelo qual forçar o compilador a embutir uma função, geralmente por meio de especificadores de declaração específicos de implementação:
- Microsoft Visual C ++:
__forceinline
- gcc ou clang:
__attribute__((always_inline))
ou__attribute__((__always_inline__))
, o último dos quais é útil para evitar um conflito com uma macro definida pelo usuário chamadaalways_inline
.
O uso indiscriminado disso pode resultar em código maior (arquivo executável inchado), ganho mínimo ou nenhum ganho de desempenho e, em alguns casos, até mesmo perda de desempenho. Além disso, o compilador não pode embutir a função em todas as circunstâncias, mesmo quando o embutir é forçado; neste caso, o gcc e o Visual C ++ geram avisos.
Forçar o inlining é útil se
-
inline
não é respeitado pelo compilador (ignorado pelo analisador de custo / benefício do compilador), e - inlining resulta em um aumento de desempenho necessário
Para portabilidade do código, as seguintes diretivas de pré-processador podem ser usadas:
#ifdef _MSC_VER
#define forceinline __forceinline
#elif defined(__GNUC__)
#define forceinline inline __attribute__((__always_inline__))
#elif defined(__CLANG__)
#if __has_attribute(__always_inline__)
#define forceinline inline __attribute__((__always_inline__))
#else
#define forceinline inline
#endif
#else
#define forceinline inline
#endif
Classes de armazenamento de funções inline
static inline
tem os mesmos efeitos em todos os dialetos C e C ++. Ele emitirá uma função visível localmente (cópia fora de linha de), se necessário.
Independentemente da classe de armazenamento, o compilador pode ignorar o inline
qualificador e gerar uma chamada de função em todos os dialetos C e C ++.
O efeito da classe de armazenamento extern
quando aplicada ou não aplicada às inline
funções difere entre os dialetos C e C ++.
C99
No C99, uma função definida inline
nunca irá, e uma função definida extern inline
irá sempre, emitir uma função externamente visível. Ao contrário do C ++, não há como solicitar que uma função externamente visível compartilhada entre as unidades de tradução seja emitida apenas se necessário.
Se as inline
declarações forem misturadas com extern inline
declarações ou com declarações não qualificadas (ou seja, sem inline
qualificador ou classe de armazenamento), a unidade de tradução deve conter uma definição (não importa se não qualificada inline
, ou extern inline
) e uma função visível externamente será emitida para ela.
Uma função definida inline
requer exatamente uma função com aquele nome em algum outro lugar do programa que está definido extern inline
ou sem qualificador. Se mais de uma definição for fornecida em todo o programa, o vinculador reclamará sobre símbolos duplicados. Se, no entanto, estiver faltando, o vinculador não necessariamente reclamará, porque, se todos os usos pudessem ser sequenciais, isso não seria necessário. Mas pode reclamar, já que o compilador sempre pode ignorar o inline
qualificador e gerar chamadas para a função, como normalmente acontece se o código for compilado sem otimização. (Este pode ser o comportamento desejado, se a função deve estar embutida em todos os lugares por todos os meios, e um erro deve ser gerado se não estiver.) Uma maneira conveniente é definir as inline
funções em arquivos de cabeçalho e criar um arquivo .c por função, contendo uma extern inline
declaração para ela e incluindo o respectivo arquivo de cabeçalho com a definição. Não importa se a declaração é anterior ou posterior à inclusão.
Para evitar que código inacessível seja adicionado ao executável final se todos os usos de uma função forem embutidos, é aconselhável colocar os arquivos objeto de todos esses arquivos .c com uma única extern inline
função em um arquivo de biblioteca estática , normalmente com ar rcs
, e vincular contra essa biblioteca em vez dos arquivos de objeto individuais. Isso faz com que sejam vinculados apenas os arquivos-objeto realmente necessários, em contraste com a vinculação direta dos arquivos-objeto, que faz com que sejam sempre incluídos no executável. No entanto, o arquivo de biblioteca deve ser especificado depois de todos os outros arquivos de objeto na linha de comando do vinculador, uma vez que as chamadas de arquivos de objeto especificados após o arquivo de biblioteca para as funções não serão consideradas pelo vinculador. As chamadas de inline
funções para outras inline
funções serão resolvidas pelo vinculador automaticamente (a s
opção em ar rcs
garante isso).
Uma solução alternativa é usar a otimização de tempo de link em vez de uma biblioteca. gcc fornece o sinalizador -Wl,--gc-sections
para omitir seções nas quais todas as funções não são utilizadas. Esse será o caso para arquivos-objeto que contêm o código de uma única extern inline
função não utilizada . No entanto, ele também remove todas e quaisquer outras seções não utilizadas de todos os outros arquivos de objeto, não apenas aquelas relacionadas a extern inline
funções não utilizadas . (Pode ser desejável vincular funções ao executável que devem ser chamadas pelo programador a partir do depurador em vez do próprio programa, por exemplo, para examinar o estado interno do programa.) Com esta abordagem, também é possível para usar um único arquivo .c com todas as extern inline
funções em vez de um arquivo .c por função. Em seguida, o arquivo deve ser compilado -fdata-sections -ffunction-sections
. No entanto, a página de manual do gcc avisa sobre isso, dizendo "Use essas opções apenas quando houver benefícios significativos em fazê-lo."
Alguns recomendam uma abordagem totalmente diferente, que é definir funções como em static inline
vez de inline
arquivos de cabeçalho. Então, nenhum código inacessível será gerado. No entanto, essa abordagem tem uma desvantagem no caso oposto: o código duplicado será gerado se a função não puder ser embutida em mais de uma unidade de tradução. O código de função emitido não pode ser compartilhado entre as unidades de tradução porque deve ter endereços diferentes. Esta é outra desvantagem; tomar o endereço de uma função definida como static inline
em um arquivo de cabeçalho produzirá valores diferentes em unidades de tradução diferentes. Portanto, as static inline
funções só devem ser usadas se forem usadas em apenas uma unidade de tradução, o que significa que devem ir apenas para o respectivo arquivo .c, e não para um arquivo de cabeçalho.
gnu89
semântica gnu89 de inline
e extern inline
são essencialmente o oposto exato daquelas em C99, com a exceção de que gnu89 permite a redefinição de uma extern inline
função como uma função não qualificada, enquanto C99 inline
não permite. Assim, gnu89 extern inline
sem redefinição é como C99 inline
, e gnu89 inline
é como C99 extern inline
; em outras palavras, no gnu89, uma função definida inline
sempre e uma função definida extern inline
nunca emitirá uma função visível externamente. A justificativa para isso é que ele corresponde a variáveis, para as quais o armazenamento nunca será reservado se definido como extern
e sempre será definido sem. A justificativa para C99, em contraste, é que seria surpreendente se o uso inline
tivesse um efeito colateral - sempre emitir uma versão não embutida da função - que é contrário ao que seu nome sugere.
As observações para C99 sobre a necessidade de fornecer exatamente uma instância de função visível externamente para funções embutidas e sobre o problema resultante com código inacessível se aplicam mutatis mutandis a gnu89 também.
O gcc até a versão 4.2, inclusive, usava a inline
semântica do gnu89 mesmo quando -std=c99
era explicitamente especificada. Com a versão 5, o gcc mudou de gnu89 para o dialeto gnu11, ativando efetivamente a inline
semântica C99 por padrão. Para usar a semântica do gnu89, eles devem ser ativados explicitamente, com -std=gnu89
ou, para afetar apenas o inlining -fgnu89-inline
, ou adicionando o gnu_inline
atributo a todas as inline
declarações. Para assegurar semântica C99, ou -std=c99
, -std=c11
, -std=gnu99
ou -std=gnu11
(sem -fgnu89-inline
) pode ser utilizado.
C ++
Em C ++, uma função definida inline
emitirá, se necessário, uma função compartilhada entre as unidades de tradução, normalmente colocando-a na seção comum do arquivo-objeto para o qual é necessária. A função deve ter a mesma definição em todos os lugares, sempre com o inline
qualificador. Em C ++, extern inline
é o mesmo que inline
. A justificativa para a abordagem C ++ é que é a maneira mais conveniente para o programador, uma vez que nenhum cuidado especial para eliminação de código inacessível deve ser tomado e, como para funções comuns, não faz diferença se extern
é especificado ou não.
O inline
qualificador é adicionado automaticamente a uma função definida como parte de uma definição de classe.
armcc
armcc no modo C90 proporciona extern inline
e inline
semântica que são os mesmos que no C ++: Tais definições irá emitir uma função compartilhada entre as unidades de tradução, se necessário. No modo C99, extern inline
sempre emite uma função, mas como no C ++, ela será compartilhada entre as unidades de tradução. Assim, a mesma função pode ser definida extern inline
em diferentes unidades de tradução. Isso corresponde ao comportamento tradicional dos compiladores Unix C para várias não- extern
definições de variáveis globais não inicializadas.
Restrições
Obter o endereço de uma inline
função requer código para que uma cópia não embutida dessa função seja emitida em qualquer caso.
No C99, uma função inline
ou extern inline
não deve acessar static
variáveis globais ou definir const
static
variáveis não locais. const static
variáveis locais podem ou não ser objetos diferentes em unidades de tradução diferentes, dependendo se a função estava embutida ou se uma chamada foi feita. Apenas as static inline
definições podem fazer referência a identificadores com ligação interna sem restrições; esses serão objetos diferentes em cada unidade de tradução. Em C ++, tanto os const
não const
static
locais quanto os não locais são permitidos e se referem ao mesmo objeto em todas as unidades de tradução.
gcc não pode funções embutidas se
- eles são variados ,
- usar
alloca
- usar computado
goto
- usar não local
goto
- usar funções aninhadas
- usar
setjmp
- usar
__builtin_longjmp
- usar
__builtin_return
, ou - usar
__builtin_apply_args
Com base nas especificações da Microsoft no MSDN, o MS Visual C ++ não pode embutir (nem mesmo com __forceinline
), se
- A função ou seu chamador é compilado com / Ob0 (a opção padrão para compilações de depuração).
- A função e o chamador usam diferentes tipos de tratamento de exceção ( tratamento de exceção C ++ em um, tratamento de exceção estruturado no outro).
- A função possui uma lista de argumentos variáveis .
- A função usa assembly embutido , a menos que compilado com / Og, / Ox, / O1 ou / O2.
- A função é recursiva e não acompanhada por
#pragma inline_recursion(on)
. Com o pragma, as funções recursivas são alinhadas a uma profundidade padrão de 16 chamadas. Para reduzir a profundidade do inlining, use oinline_depth
pragma. - A função é virtual e é chamada virtualmente. As chamadas diretas para funções virtuais podem ser embutidas.
- O programa obtém o endereço da função e a chamada é feita por meio do ponteiro para a função. As chamadas diretas para funções cujos endereços foram atendidos podem ser sequenciais.
- A função também é marcada com o
__declspec
modificador nu .
Problemas
Além dos problemas com a expansão inline em geral (consulte Expansão inline § Efeito no desempenho ), as inline
funções como um recurso de linguagem podem não ser tão valiosas quanto parecem, por uma série de razões:
- Freqüentemente, um compilador está em uma posição melhor do que um humano para decidir se uma função específica deve ser embutida. Às vezes, o compilador pode não ser capaz de embutir tantas funções quanto o programador indica.
- Um ponto importante a notar é que o código (da
inline
função) fica exposto ao seu cliente (a função de chamada). - Conforme as funções evoluem, eles podem se tornar adequados para inlining onde antes, ou não mais adequados para inlining onde estavam. Embora inlining ou un-inlining uma função seja mais fácil do que converter de e para macros, ainda requer manutenção extra que normalmente produz relativamente poucos benefícios.
- As funções inline usadas na proliferação em sistemas de compilação baseados em C nativos podem aumentar o tempo de compilação, uma vez que a representação intermediária de seus corpos é copiada em cada site de chamada.
- A especificação
inline
em C99 requer exatamente uma definição externa da função, se for usada em algum lugar. Se tal definição não foi fornecida pelo programador, isso pode facilmente levar a erros do vinculador. Isso pode acontecer com a otimização desativada, o que normalmente impede o inlining. Adicionar as definições, por outro lado, pode causar código inacessível se o programador não o evita cuidadosamente, colocando-as em uma biblioteca para vinculação, usando a otimização de tempo de link oustatic inline
. - Em C ++, é necessário definir uma
inline
função em cada módulo (unidade de tradução) que a utiliza, enquanto uma função comum deve ser definida em apenas um único módulo. Caso contrário, não seria possível compilar um único módulo independentemente de todos os outros módulos. Dependendo do compilador, isso pode fazer com que cada arquivo de objeto respectivo contenha uma cópia do código da função, para cada módulo com algum uso que não poderia ser embutido. - Em software embarcado , muitas vezes certas funções precisam ser colocadas em certas seções de código pelo uso de instruções especiais do compilador, como instruções "pragma". Às vezes, uma função em um segmento de memória pode precisar chamar uma função em outro segmento de memória e, se ocorrer inlining da função chamada, o código da função chamada pode terminar em um segmento onde não deveria estar. Por exemplo, segmentos de memória de alto desempenho podem ser muito limitados no espaço de código, e se uma função pertencente a tal espaço chamar outra grande função que não se destina a estar na seção de alto desempenho e a função chamada ficar inadequadamente alinhada, então isso pode fazer com que o segmento de memória de alto desempenho fique sem espaço de código. Por esse motivo, às vezes é necessário garantir que as funções não fiquem embutidas.
Citações
- "Uma declaração de função [...] Com um especificador embutido declara uma função embutida. O especificador embutido indica para a implementação que a substituição embutida do corpo da função no ponto de chamada deve ser preferida ao mecanismo de chamada de função usual. Uma implementação não é necessário realizar esta substituição em linha no ponto de chamada; no entanto, mesmo que esta substituição em linha seja omitida, as outras regras para funções em linha definidas em 7.1.2 ainda devem ser respeitadas. "
- - ISO / IEC 14882: 2011, o padrão C ++ atual, seção 7.1.2
- "Uma função declarada com um especificador de função embutida é uma função embutida. [...] Tornar uma função uma função embutida sugere que as chamadas para a função sejam o mais rápidas possível. A extensão em que essas sugestões são eficazes é definida pela implementação ( nota de rodapé: por exemplo, uma implementação pode nunca realizar substituições in-line ou pode realizar apenas substituições in-line para chamadas no escopo de uma declaração in-line. )
- "[...] Uma definição embutida não fornece uma definição externa para a função e não proíbe uma definição externa em outra unidade de tradução . Uma definição embutida fornece uma alternativa para uma definição externa, que um tradutor pode usar para implementar qualquer chamada para a função na mesma unidade de tradução. Não é especificado se uma chamada para a função usa a definição embutida ou a definição externa. "
- - ISO 9899: 1999 (E), o padrão C99, seção 6.7.4
Veja também
Referências
- JANA, DEBASISH (1 de janeiro de 2005). C ++ E PARADIGMA DE PROGRAMAÇÃO ORIENTADA A OBJETOS . PHI Learning Pvt. Ltd. ISBN 978-81-203-2871-6.
- Sengupta, Probal (1 de agosto de 2004). Programação orientada a objetos: fundamentos e aplicações . PHI Learning Pvt. Ltd. ISBN 978-81-203-1258-6.
- Svenk, Goran (2003). Programação Orientada a Objetos: Usando C ++ para Engenharia e Tecnologia . Cengage Learning. ISBN 0-7668-3894-3.
- Balagurusamy (2013). Programação Orientada a Objetos com C ++ . Educação Tata McGraw-Hill. ISBN 978-1-259-02993-6.
- Kirch-Prinz, Ulla; Prinz, Peter (2002). Um guia completo para programação em C ++ . Jones e Bartlett Learning. ISBN 978-0-7637-1817-6.
- Conger, David (2006). Criando jogos em C ++: um guia passo a passo . Novos pilotos. ISBN 978-0-7357-1434-2.
- Skinner, MT (1992). O livro C ++ avançado . Silicon Press. ISBN 978-0-929306-10-0.
- Love (1 de setembro de 2005). Desenvolvimento do Kernel Linux . Pearson Education. ISBN 978-81-7758-910-8.
- DEHURI, SATCHIDANANDA; JAGADEV, ALOK KUMAR; RATH, AMIYA KUMAR (8 de maio de 2007). PROGRAMAÇÃO ORIENTADA A OBJETOS USANDO C ++ . PHI Learning Pvt. Ltd. ISBN 978-81-203-3085-6.
links externos
- Funções inline com GNU Compiler Collection (GCC)
- Resumo da semântica "inline" em C e C ++ , pelo contribuidor do LLVM David Chisnall