Criar uma função a staticoculta de outras unidades de tradução, o que ajuda a fornecer o encapsulamento .
helper_file.c
int f1(int);/* prototype */staticint f2(int);/* prototype */int f1(int foo){return f2(foo);/* ok, f2 is in the same translation unit *//* (basically same .c file) as f1 */}int f2(int foo){return42+ foo;}
main.c :
int f1(int);/* prototype */int f2(int);/* prototype */int main(void){
f1(10);/* ok, f1 is visible to the linker */
f2(12);/* nope, f2 is not visible to the linker */return0;}
A unidade de tradução é a terminologia correta a ser usada aqui? O arquivo de objeto não seria mais preciso? Pelo que entendi, uma função estática está oculta do vinculador e o vinculador não opera em unidades de conversão.
Steven Eckhoff
2
Eu também deveria ter dito que gosto de pensar nisso como oculto no vinculador; parece mais claro assim.
Steven Eckhoff
1
então, função interna (que com certeza não a chamamos fora de seu arquivo c), devemos colocá-la como função estática, certo? Portanto, podemos ter certeza de que não pode ligar para outro lugar. Obrigado :)
hqt
1
Como você compila isso? Você usa #include <helper_file.c>? Eu acho que isso tornaria uma única unidade de tradução então ...
Atcold
2
@Atcold: da maneira que escrevi o código, você simplesmente inclui os 2 arquivos de origem na linha de comando, assim gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c. Os protótipos para as funções estão presentes nos dois arquivos de origem (não há necessidade de arquivos de cabeçalho). O vinculador resolverá as funções.
pmg
80
pmg é direto sobre encapsulamento; além de ocultar a função de outras unidades de conversão (ou melhor, por causa disso), as funções de criação statictambém podem conferir benefícios de desempenho na presença de otimizações do compilador.
Como uma staticfunção não pode ser chamada de qualquer lugar fora da unidade de tradução atual (a menos que o código leve um ponteiro para seu endereço), o compilador controla todos os pontos de chamada nela.
Isso significa que é livre usar uma ABI fora do padrão, incorporá-la inteiramente ou executar qualquer número de outras otimizações que talvez não sejam possíveis para uma função com ligação externa.
@caf O que você quer dizer com endereço da função? Para mim, a noção de funções / variáveis com endereços ou sendo atribuídas a endereços em tempo de compilação é um pouco confusa. Você pode por favor elaborar?
SayeedHussain
2
@ codificador criptográfico: Seu programa está carregado na memória, portanto, as funções também têm um local de memória e o endereço pode ser obtido. Com um ponteiro de função, você pode chamar qualquer um desses. Se você fizer isso, reduz a lista de otimizações que o compilador pode executar, pois o código deve permanecer intacto no mesmo local.
5
@ codypticcoder: quero dizer que uma expressão avalia um ponteiro para a função e faz algo com ela que não seja chamar imediatamente a função. Se um ponteiro para uma staticfunção escapar da unidade de tradução atual, essa função poderá ser chamada diretamente de outras unidades de tradução.
Caf
@caf se o endereço da função for escolhido, o compilador detectaria isso e desativaria as otimizações de função estática mencionadas nesta resposta (por exemplo, usando uma ABI não padrão)? Suponho que teria que ser.
sevko 12/07/19
28
A staticpalavra-chave em C é usada em um arquivo compilado (.c em oposição a .h) para que a função exista apenas nesse arquivo.
Normalmente, quando você cria uma função, o compilador gera um cruft que o vinculador pode usar para, bem, vincular uma chamada de função a essa função. Se você usar a palavra-chave estática, outras funções no mesmo arquivo poderão chamar essa função (porque isso pode ser feito sem o recurso ao vinculador), enquanto o vinculador não possui informações que permitam que outros arquivos acessem a função.
Programadores C usam o atributo static para ocultar declarações de variáveis e funções dentro de módulos, da mesma forma que você usaria declarações públicas e privadas em Java e C ++. Os arquivos de origem C desempenham o papel de módulos. Qualquer variável global ou função declarada com o atributo estático é privada para esse módulo. Da mesma forma, qualquer variável global ou função declarada sem o atributo estático é pública e pode ser acessada por qualquer outro módulo. É uma boa prática de programação proteger suas variáveis e funções com o atributo estático sempre que possível.
A resposta da pmg é muito convincente. Se você deseja saber como as declarações estáticas funcionam no nível do objeto, as informações abaixo podem ser interessantes para você. Reutilizei o mesmo programa escrito por pmg e o compilei em um arquivo .so (objeto compartilhado)
O conteúdo a seguir é depois de despejar o arquivo .so em algo legível por humanos
0000000000000675 f1 : endereço da função f1
000000000000068c f2 : endereço da função f2 (staticc)
observe a diferença no endereço da função, isso significa alguma coisa. Para uma função declarada com endereço diferente, pode muito bem significar que f2 mora muito longe ou em um segmento diferente do arquivo de objeto.
Os vinculadores usam algo chamado PLT (tabela de vinculação de procedimentos) e GOT (tabela de compensações globais) para entender os símbolos aos quais eles têm acesso.
Por enquanto, pense que GOT e PLT vinculam magicamente todos os endereços e uma seção dinâmica contém informações de todas essas funções que são visíveis pelo vinculador.
Depois de despejar a seção dinâmica do arquivo .so, obtemos várias entradas, mas apenas interessadas na função f1 e f2 .
A seção dinâmica mantém a entrada apenas para a função f1 no endereço 0000000000000675 e não para f2 !
Num: Valor Tamanho Tipo Vinculação Vis Ndx Name
9:000000000000067523 FUNC GLOBAL DEFAULT 11 f1
E é isso !. A partir disso, fica claro que o vinculador não conseguiu encontrar a função f2 , pois não está na seção dinâmica do arquivo .so.
Quando for necessário restringir o acesso a algumas funções, usaremos a palavra-chave estática ao definir e declarar uma função.
/* file ab.c */staticvoid function1(void){
puts("function1 called");}And store the following code in another file ab1.c
/* file ab1.c */int main(void){
function1();
getchar();return0;}/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */
Respostas:
Criar uma função a
static
oculta de outras unidades de tradução, o que ajuda a fornecer o encapsulamento .helper_file.c
main.c :
fonte
#include <helper_file.c>
? Eu acho que isso tornaria uma única unidade de tradução então ...gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. Os protótipos para as funções estão presentes nos dois arquivos de origem (não há necessidade de arquivos de cabeçalho). O vinculador resolverá as funções.pmg é direto sobre encapsulamento; além de ocultar a função de outras unidades de conversão (ou melhor, por causa disso), as funções de criação
static
também podem conferir benefícios de desempenho na presença de otimizações do compilador.Como uma
static
função não pode ser chamada de qualquer lugar fora da unidade de tradução atual (a menos que o código leve um ponteiro para seu endereço), o compilador controla todos os pontos de chamada nela.Isso significa que é livre usar uma ABI fora do padrão, incorporá-la inteiramente ou executar qualquer número de outras otimizações que talvez não sejam possíveis para uma função com ligação externa.
fonte
static
função escapar da unidade de tradução atual, essa função poderá ser chamada diretamente de outras unidades de tradução.A
static
palavra-chave em C é usada em um arquivo compilado (.c em oposição a .h) para que a função exista apenas nesse arquivo.Normalmente, quando você cria uma função, o compilador gera um cruft que o vinculador pode usar para, bem, vincular uma chamada de função a essa função. Se você usar a palavra-chave estática, outras funções no mesmo arquivo poderão chamar essa função (porque isso pode ser feito sem o recurso ao vinculador), enquanto o vinculador não possui informações que permitam que outros arquivos acessem a função.
fonte
Olhando para as postagens acima, gostaria de apontar um detalhe.
Suponha que nosso arquivo principal ("main.c") seja assim:
Agora considere três casos:
Caso 1: Nosso arquivo de cabeçalho ("header.h") fica assim:
Em seguida, o seguinte comando no linux:
terá sucesso ! Depois disso, se alguém executar
A saída será
Chamando a função dentro do cabeçalho
Qual é o que essa função estática deve imprimir.
Caso 2: Nosso arquivo de cabeçalho ("header.h") se parece com o seguinte:
e também temos mais um arquivo "header.c", que se parece com isso:
Em seguida, o seguinte comando
dará um erro.
Caso 3:
Semelhante ao caso 2, exceto que agora nosso arquivo de cabeçalho ("header.h") é:
Então, o mesmo comando do caso 2 será bem-sucedido e a execução de ./main continuará fornecendo o resultado esperado.
Portanto, a partir desses testes (executados na máquina Acer x86, Ubuntu OS), assumi que
A palavra-chave estática impede que a função seja chamada em outro arquivo * .c que não seja onde está definido.
Corrija-me se eu estiver errada.
fonte
Programadores C usam o atributo static para ocultar declarações de variáveis e funções dentro de módulos, da mesma forma que você usaria declarações públicas e privadas em Java e C ++. Os arquivos de origem C desempenham o papel de módulos. Qualquer variável global ou função declarada com o atributo estático é privada para esse módulo. Da mesma forma, qualquer variável global ou função declarada sem o atributo estático é pública e pode ser acessada por qualquer outro módulo. É uma boa prática de programação proteger suas variáveis e funções com o atributo estático sempre que possível.
fonte
A resposta da pmg é muito convincente. Se você deseja saber como as declarações estáticas funcionam no nível do objeto, as informações abaixo podem ser interessantes para você. Reutilizei o mesmo programa escrito por pmg e o compilei em um arquivo .so (objeto compartilhado)
O conteúdo a seguir é depois de despejar o arquivo .so em algo legível por humanos
0000000000000675 f1 : endereço da função f1
000000000000068c f2 : endereço da função f2 (staticc)
observe a diferença no endereço da função, isso significa alguma coisa. Para uma função declarada com endereço diferente, pode muito bem significar que f2 mora muito longe ou em um segmento diferente do arquivo de objeto.
Os vinculadores usam algo chamado PLT (tabela de vinculação de procedimentos) e GOT (tabela de compensações globais) para entender os símbolos aos quais eles têm acesso.
Por enquanto, pense que GOT e PLT vinculam magicamente todos os endereços e uma seção dinâmica contém informações de todas essas funções que são visíveis pelo vinculador.
Depois de despejar a seção dinâmica do arquivo .so, obtemos várias entradas, mas apenas interessadas na função f1 e f2 .
A seção dinâmica mantém a entrada apenas para a função f1 no endereço 0000000000000675 e não para f2 !
Num: Valor Tamanho Tipo Vinculação Vis Ndx Name
E é isso !. A partir disso, fica claro que o vinculador não conseguiu encontrar a função f2 , pois não está na seção dinâmica do arquivo .so.
fonte
Quando for necessário restringir o acesso a algumas funções, usaremos a palavra-chave estática ao definir e declarar uma função.
fonte