Quando tento construir este código
inline void f() {}
int main()
{
f();
}
usando a linha de comando
gcc -std=c99 -o a a.c
Recebo um erro do vinculador (referência indefinida para f
). O erro desaparece se eu usar static inline
ou em extern inline
vez de apenas inline
, ou se eu compilar com -O
(portanto, a função é realmente embutida).
Este comportamento parece estar definido no parágrafo 6.7.4 (6) da norma C99:
Se todas as declarações de escopo de arquivo para uma função em uma unidade de tradução incluem o
inline
especificador de função semextern
, então a definição nessa unidade de tradução é uma definição embutida. 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 a uma definição externa, que um tradutor pode usar para implementar qualquer chamada à 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.
Se bem entendi tudo isso, uma unidade de compilação com uma função definida inline
como no exemplo acima só compila de forma consistente se houver também uma função externa com o mesmo nome, e nunca sei se minha própria função ou a função externa é chamada.
Este comportamento não é completamente idiota? É útil definir uma função inline
sem static
ou extern
em C99? Estou esquecendo de algo?
Resumo das respostas
Claro que estava faltando alguma coisa, e o comportamento não é idiota. :)
Como explica Nemo , a ideia é colocar a definição da função
inline void f() {}
no arquivo de cabeçalho e apenas uma declaração
extern inline void f();
no arquivo .c correspondente. Apenas a extern
declaração aciona a geração de código binário visível externamente. E de fato não há uso deinline
em um arquivo .c - só é útil em cabeçalhos.
Como explica o raciocínio do comitê C99 citado na resposta de Jonathan , inline
tudo gira em torno de otimizações do compilador que requerem a definição de uma função para ser visível no local de uma chamada. Isso só pode ser alcançado colocando a definição no cabeçalho e, claro, uma definição em um cabeçalho não deve emitir código toda vez que for vista pelo compilador. Mas, como o compilador não é forçado a embutir uma função, uma definição externa deve existir em algum lugar.
inline
semstatic
eextern
, no entanto. Infelizmente, nenhum desses problemas é abordado nessa questão.Respostas:
Na verdade, esta excelente resposta também responde à sua pergunta, eu acho:
O que extern inline faz?
A ideia é que "embutido" pode ser usado em um arquivo de cabeçalho e, em seguida, "embutido externo" em um arquivo .c. "extern inline" é apenas como você instrui o compilador qual arquivo-objeto deve conter o código gerado (visível externamente).
[atualizar, para elaborar]
Eu não acho que haja qualquer uso para "inline" (sem "estático" ou "externo") em um arquivo .c. Mas, em um arquivo de cabeçalho, faz sentido e requer uma declaração "extern inline" correspondente em algum arquivo .c para realmente gerar o código independente.
fonte
inline void f() {}
no cabeçalho eextern inline void f();
no arquivo .c? Portanto, a definição real da função vai no cabeçalho e o arquivo .c contém uma mera declaração neste caso, no reverso da ordem usual?inline
semstatic
ouextern
. Claro questatic inline
está bem, mas não é disso que se trata esta pergunta e resposta.-std=c99
vez de-std=gnu89
.Do próprio padrão (ISO / IEC 9899: 1999):
O Comitê C99 escreveu uma justificativa e diz:
fonte
> Recebo um erro de vinculador (referência indefinida paraf
)Funciona aqui: Linux x86-64, GCC 4.1.2. Pode ser um bug em seu compilador; Não vejo nada no parágrafo citado da norma que proíba o programa em questão. Observe o uso de if em vez de iff .Portanto, se você conhece o comportamento da função
f
e deseja chamá-la em um loop fechado, pode copiar e colar sua definição em um módulo para evitar chamadas de função; ou , você pode fornecer uma definição que, para os propósitos do módulo atual, seja equivalente (mas pula a validação de entrada ou qualquer otimização que você possa imaginar). O gravador do compilador, entretanto, tem a opção de otimizar o tamanho do programa.fonte