Efeitos da palavra-chave externa em funções C

Em C, não notei nenhum efeito da palavra-chave extern usada antes da declaração da function. No começo, pensei que ao definir extern int f(); em um único arquivo força você a implementá-lo fora do escopo do arquivo. No entanto, descobri que ambos:

 extern int f(); int f() {return 0;} 

e

 extern int f() {return 0;} 

compilar muito bem, sem avisos do gcc. Eu usei o gcc -Wall -ansi ; nem aceitaria comentários.

Existem efeitos para usar extern antes das definições de function ? Ou é apenas uma palavra-chave opcional sem efeitos colaterais para funções.

No último caso, não entendo por que os designers-padrão escolheram desordenar a gramática com palavras-chave supérfluas.

EDIT: Para esclarecer, eu sei que há uso para extern em variables, mas estou apenas perguntando sobre funções extern .

Nós temos dois arquivos, foo.c e bar.c.

Aqui está foo.c

 #include  volatile unsigned int stop_now = 0; extern void bar_function(void); int main(void) { while (1) { bar_function(); stop_now = 1; } return 0; } 

Agora, aqui é bar.c

 #include  extern volatile unsigned int stop_now; void bar_function(void) { while (! stop_now) { printf("Hello, world!\n"); sleep(30); } } 

Como você pode ver, não temos nenhum header compartilhado entre foo.c e bar.c, no entanto, o bar.c precisa de algo declarado no foo.c quando está vinculado, e o foo.c precisa de uma function do bar.c quando está vinculado.

Usando ‘extern’, você está dizendo ao compilador que o que quer que o segue será encontrado (não-estático) na hora do link; não reserve nada para isso no passe atual, pois ele será encontrado mais tarde. Funções e variables ​​são tratadas igualmente a esse respeito.

É muito útil se você precisar compartilhar algum global entre os módulos e não quiser colocar / inicializá-lo em um header.

Tecnicamente, todas as funções em um header público da biblioteca são ‘externas’, entretanto rotulá-las como tal tem muito pouco ou nenhum benefício, dependendo do compilador. A maioria dos compiladores pode descobrir isso por conta própria. Como você vê, essas funções são realmente definidas em outro lugar.

No exemplo acima, main () imprimiria hello world apenas uma vez, mas continuaria a entrar em bar_function (). Observe também que bar_function () não retornará neste exemplo (já que é apenas um exemplo simples). Apenas imagine stop_now ser modificado quando um sinal é atendido (portanto, volátil) se isso não parecer prático o suficiente.

Externs são muito úteis para coisas como manipuladores de sinais, um mutex que você não quer colocar em um header ou estrutura, etc. A maioria dos compiladores irá otimizar para garantir que eles não reservem memory para objects externos, já que eles sabem que eles Estará reservando-o no módulo onde o object está definido. No entanto, novamente, não há sentido em especificá-lo com os compiladores modernos ao criar protótipos de funções públicas.

Espero que ajude 🙂

Tanto quanto me lembro do padrão, todas as declarações de function são consideradas como “externas” por padrão, então não há necessidade de especificá-lo explicitamente.

Isso não torna essa palavra-chave inútil, já que ela também pode ser usada com variables ​​(e nesse caso – é a única solução para resolver problemas de vinculação). Mas com as funções – sim, é opcional.

Você precisa distinguir entre dois conceitos separados: definição de function e declaração de símbolo. “extern” é um modificador de binding, uma dica para o compilador sobre onde o símbolo referido posteriormente é definido (a dica é “não aqui”).

Se eu escrever

 extern int i; 

no escopo do arquivo (fora de um bloco de funções) em um arquivo C, você está dizendo “a variável pode ser definida em outro lugar”.

 extern int f() {return 0;} 

é uma declaração da function f e uma definição da function f. A definição, neste caso, ultrapassa o externo.

 extern int f(); int f() {return 0;} 

é primeiro uma declaração, seguida da definição.

O uso de extern está errado se você deseja declarar e definir simultaneamente uma variável de escopo de arquivo. Por exemplo,

 extern int i = 4; 

dará um erro ou aviso, dependendo do compilador.

O uso de extern é útil se você quiser explicitamente evitar a definição de uma variável.

Deixe-me explicar:

Digamos que o arquivo ac contenha:

 #include "ah" int i = 2; int f() { i++; return i;} 

O arquivo ah inclui:

 extern int i; int f(void); 

e o arquivo bc contém:

 #include  #include "ah" int main(void){ printf("%d\n", f()); return 0; } 

O extern no header é útil, porque diz ao compilador durante a fase de link, “isto é uma declaração e não uma definição”. Se eu remover a linha em ac, que define i, aloca espaço para ela e atribui um valor a ela, o programa não deve compilar com uma referência indefinida. Isso diz ao desenvolvedor que ele se referiu a uma variável, mas ainda não a definiu. Se, por outro lado, eu omitir a palavra-chave “extern” e remover a linha int i = 2 , o programa ainda compilará – será definido com um valor padrão de 0.

Variáveis ​​de escopo de arquivo são implicitamente definidas com um valor padrão de 0 ou NULL se você não atribuir explicitamente um valor a elas – diferentemente das variables ​​de escopo de bloco que você declara no topo de uma function. A palavra-chave externa evita essa definição implícita e, portanto, ajuda a evitar erros.

Para funções, em declarações de function, a palavra-chave é de fato redundante. Declarações de function não possuem uma definição implícita.

A palavra-chave extern assume diferentes formas, dependendo do ambiente. Se uma declaração estiver disponível, a palavra-chave extern a binding como a especificada anteriormente na unidade de tradução. Na ausência de tal declaração, extern especifica a binding externa.

 static int g(); extern int g(); /* g has internal linkage */ extern int j(); /* j has tentative external linkage */ extern int h(); static int h(); /* error */ 

Aqui estão os parágrafos relevantes do rascunho do C99 (n1256):

6.2.2 Ligações de identificadores

[…]

4 Para um identificador declarado com o especificador de class de armazenamento externo em um escopo no qual uma declaração anterior desse identificador é visível, 23) se a declaração anterior especifica binding interna ou externa, a binding do identificador na declaração posterior é a mesma como a binding especificada na declaração prévia. Se nenhuma declaração anterior estiver visível, ou se a declaração anterior não especificar nenhuma binding, o identificador terá uma binding externa.

5 Se a declaração de um identificador para uma function não possui nenhum especificador de class de armazenamento, sua binding é determinada exatamente como se fosse declarada com o especificador de class de armazenamento externo. Se a declaração de um identificador para um object tiver escopo de arquivo e nenhum especificador de class de armazenamento, sua binding será externa.

Funções embutidas possuem regras especiais sobre o que significa extern . (Note que as funções embutidas são uma extensão C99 ou GNU; elas não estavam no original C.

Para funções não embutidas, o extern não é necessário, pois está ativado por padrão.

Observe que as regras para C ++ são diferentes. Por exemplo, extern "C" é necessário na declaração C ++ das funções C que você chamará do C ++, e existem regras diferentes sobre inline .

A palavra-chave extern informa ao compilador que a function ou variável possui binding externa – em outras palavras, que é visível a partir de arquivos diferentes daquele em que está definida. Nesse sentido, tem o significado oposto à palavra-chave static . É um pouco estranho colocar extern no momento da definição, já que nenhum outro arquivo teria visibilidade da definição (ou resultaria em múltiplas definições). Normalmente você coloca extern em uma declaração em algum ponto com visibilidade externa (como um arquivo de header) e coloca a definição em outro lugar.

declarar uma function extern significa que sua definição será resolvida no momento da vinculação, não durante a compilation.

Ao contrário das funções regulares, que não são declaradas externamente, elas podem ser definidas em qualquer um dos arquivos de origem (mas não em vários arquivos de origem; caso contrário, você receberá um erro do vinculador dizendo que forneceu várias definições da function) qual é declarado extern.So, em seu caso o linker resolve a definição da function no mesmo arquivo.

Eu não acho que fazer isso seria muito útil, no entanto, fazer esse tipo de experimento fornece uma visão melhor sobre como o compilador e o linker da linguagem funcionam.

A razão pela qual não tem efeito é porque no link-time o linker tenta resolver a definição externa (no seu caso extern int f() ). Não importa se o encontra no mesmo arquivo ou em um arquivo diferente, desde que seja encontrado.

espero que isso responda sua pergunta.