O que significa "estático" em C?


1138

Eu já vi a palavra staticusada em diferentes lugares no código C; é como uma função / classe estática em C # (onde a implementação é compartilhada entre objetos)?



15
Qual é o motivo para remover "em um programa C" do final do título, @Lundin? É um pouco redundante na presença da tag c , mas me permite ver a categorização mais rapidamente, sem inspecionar as tags. Essa redundância é muito confortável quando chego à pergunta de uma direção que também pode conter perguntas sobre outros idiomas, por exemplo, pesquisa estática ou no Google.
Palec

5
@Palec Existe uma política de SO em que os itens presentes na lista de tags são redundantes no título. O site anexará automaticamente C ao site real. Um Google para "C estático" fornece essa resposta como principal hit. A razão pela qual isso foi alterado é porque essa pergunta agora faz parte das perguntas frequentes do idioma SO C e todas as postagens adicionadas são um pouco polidas.
Lundin 23/05

1
@Lundin Eu prefiro manter "C" no título, porque o SO anexa apenas uma tag ao título (a mais comum?). E se algum dia a "sintaxe" chegar a mais perguntas do que C (já que é uma coisa entre idiomas)? Eu prefiro usar o comportamento explícito :-) Edit: ah, mas há uma questão meta dizendo o contrário: meta.stackexchange.com/questions/19190/...
Ciro Santilli郝海东冠状病六四事件法轮功

Respostas:


1520
  1. Uma variável estática dentro de uma função mantém seu valor entre invocações.
  2. Uma variável global estática ou uma função é "vista" apenas no arquivo declarado em

(1) é o tópico mais externo se você é um novato, então aqui está um exemplo:

#include <stdio.h>

void foo()
{
    int a = 10;
    static int sa = 10;

    a += 5;
    sa += 5;

    printf("a = %d, sa = %d\n", a, sa);
}


int main()
{
    int i;

    for (i = 0; i < 10; ++i)
        foo();
}

Isso imprime:

a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60

Isso é útil nos casos em que uma função precisa manter algum estado entre as invocações e você não deseja usar variáveis ​​globais. Cuidado, no entanto, esse recurso deve ser usado com moderação - torna seu código não seguro para threads e mais difícil de entender.

(2) É amplamente utilizado como um recurso de "controle de acesso". Se você tem um arquivo .c implementando alguma funcionalidade, normalmente expõe apenas algumas funções "públicas" para os usuários. O restante de suas funções deve ser feito static, para que o usuário não possa acessá-las. Isso é encapsulamento, uma boa prática.

Citando a Wikipedia :

Na linguagem de programação C, estática é usada com variáveis ​​e funções globais para definir seu escopo para o arquivo que contém. Nas variáveis ​​locais, estático é usado para armazenar a variável na memória alocada estaticamente, em vez da memória alocada automaticamente. Enquanto o idioma não determina a implementação de qualquer tipo de memória, a memória alocada estaticamente é normalmente reservada no segmento de dados do programa em tempo de compilação, enquanto a memória alocada automaticamente é normalmente implementada como uma pilha de chamadas transitórias.

E para responder sua segunda pergunta, não é como em C #.

No C ++, no entanto, statictambém é usado para definir atributos de classe (compartilhados entre todos os objetos da mesma classe) e métodos. Em C não há classes, portanto esse recurso é irrelevante.


179
Pax, o OP não sabe sobre estática, então você sugere mergulhá-lo na diferença entre unidades e arquivos de compilação? :-)
Eli Bendersky

138
Uma unidade de compilação é um único arquivo que o compilador vê. Seu arquivo .c pode incluir outros arquivos .c, mas após o pré-processador classificar as inclusões, o compilador eventualmente vê apenas uma única "unidade de compilação".
Eli Bendersky

81
@robUK: o compilador nem sabe sobre os arquivos .h - eles são combinados nos arquivos .c no pré-processador. Portanto, sim, você pode dizer que o arquivo .c, com todos os cabeçalhos incluídos, é uma única unidade de compilação.
Eli Bendersky

6
@ TonyD talvez seja confuso, mas é assim que a compilação funciona. Normalmente, pode haver um .cmonte de arquivos de cabeçalho, mas o diabo está sempre no que não é típico.
peterph

7
@TonyD O compilador faz a compilação. O pré-processador faz o pré-processamento. Chamar o conjunto de ferramentas de 'compilador' não muda o que é ou o que faz.
Route de milhas

231

Há mais um uso não abordado aqui, e isso é parte de uma declaração de tipo de matriz como argumento para uma função:

int someFunction(char arg[static 10])
{
    ...
}

Nesse contexto, isso especifica que os argumentos passados ​​para essa função devem ser uma matriz do tipo charcom pelo menos 10 elementos. Para mais informações, veja minha pergunta aqui .


3
Eu não acho que C tinha argumentos de matriz? Linus Torvalds reclama com raiva sobre as pessoas fazendo isso.
Suprjami 29/09/2015

13
@jamieb: C não têm argumentos da matriz, mas esse meio de sintaxe específicas que a função espera arg[0]até arg[9]a ter valores (o que também implica que a função não aceitar um ponteiro nulo). Os compiladores podem utilizar essas informações de alguma forma para otimização, e os analisadores estáticos podem utilizar essas informações para garantir que a função nunca receba um ponteiro nulo (ou, se puder dizer, uma matriz com menos elementos do que o especificado).
dreamlax

19
@Qix - Este era um novo significado sobrecarregado dado staticem C99. Tem mais de uma década e meia de idade, mas nem todos os escritores de compiladores adotaram todos os recursos do C99 - portanto, o C99 como um todo permanece amplamente desconhecido.
Happy Green Kid Naps

@suprjami Eu não tenho 100% de certeza do que você quer dizer com "argumentos da matriz" , mas se você quer dizer int arr[n];, essa é uma VLA (matriz de comprimento variável) , que foi adicionada na C99. É isso que você quis dizer?
RastaJedi

170

Resposta curta ... depende.

  1. Variáveis ​​locais definidas estáticas não perdem seu valor entre as chamadas de função. Em outras palavras, são variáveis ​​globais, mas com escopo definido para a função local em que estão definidos.

  2. Variáveis ​​globais estáticas não são visíveis fora do arquivo C em que estão definidas.

  3. Funções estáticas não são visíveis fora do arquivo C em que estão definidas.


8
Então, "função estática" e "função privada" significam a mesma coisa? Da mesma forma, "variáveis ​​globais estáticas" e "variáveis ​​globais privadas" são a mesma coisa?
user1599964

40
Trata-se de C. Não há privado / público em C.
chris

19
@ user1599964 embora não exista privateem C, sua analogia é boa: estática torna as coisas "privadas" para um determinado arquivo. E os arquivos em C frequentemente são mapeados para classes em C ++.
Ciro Santilli adicionou uma nova foto

67

Exemplo de escopo variável de vários arquivos

Aqui ilustro como a estática afeta o escopo das definições de função em vários arquivos.

ac

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
/programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*int i = 0;*/

/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/

/* OK: extern. Will use the one in main. */
extern int i;

/* OK: only visible to this file. */
static int si = 0;

void a() {
    i++;
    si++;
    puts("a()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

main.c

#include <stdio.h>

int i = 0;
static int si = 0;

void a();    

void m() {
    i++;
    si++;
    puts("m()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

int main() {
    m();
    m();
    a();
    a();
    return 0;
}

GitHub upstream .

Compile e execute:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o

Resultado:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

Interpretação

  • existem duas variáveis ​​separadas para si, uma para cada arquivo
  • existe uma única variável compartilhada para i

Como de costume, quanto menor o escopo, melhor, sempre declare variáveis static se puder.

Na programação C, os arquivos são frequentemente usados ​​para representar "classes" e as staticvariáveis ​​representam membros estáticos privados da classe.

O que os padrões dizem sobre isso

C99 N1256 draft 6.7.1 "Especificadores da classe de armazenamento" diz que staticé um "especificador da classe de armazenamento".

6.2.2 / 3 "Ligações de identificadores" diz staticimplica internal linkage:

Se a declaração de um identificador de escopo de arquivo para um objeto ou uma função contiver o estático do especificador de classe de armazenamento, o identificador terá ligação interna.

e 6.2.2 / 2 diz que internal linkagese comporta como no nosso exemplo:

No conjunto de unidades de tradução e bibliotecas que constituem um programa inteiro, cada declaração de um identificador específico com ligação externa indica o mesmo objeto ou função. Dentro de uma unidade de conversão, cada declaração de um identificador com ligação interna indica o mesmo objeto ou função.

onde "a unidade de tradução é um arquivo de origem após o pré-processamento.

Como o GCC o implementa para o ELF (Linux)?

Com o STB_LOCAL ligação.

Se compilarmos:

int i = 0;
static int si = 0;

e desmonte a tabela de símbolos com:

readelf -s main.o

a saída contém:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si
 10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i

portanto, a ligação é a única diferença significativa entre eles. Valueé apenas o deslocamento para a .bssseção, portanto esperamos que seja diferente.

STB_LOCALestá documentado na especificação ELF em http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :

STB_LOCAL Os símbolos locais não são visíveis fora do arquivo de objeto que contém sua definição. Símbolos locais com o mesmo nome podem existir em vários arquivos sem interferir entre si

o que a torna uma escolha perfeita para representar static.

Variáveis ​​sem estática são STB_GLOBALe a especificação diz:

Quando o editor de links combina vários arquivos de objetos realocáveis, ele não permite várias definições de símbolos STB_GLOBAL com o mesmo nome.

que é coerente com os erros de link em várias definições não estáticas.

Se acionarmos a otimização -O3, o sisímbolo será removido inteiramente da tabela de símbolos: ele não pode ser usado de fora de qualquer maneira. TODO por que manter variáveis ​​estáticas na tabela de símbolos quando não há otimização? Eles podem ser usados ​​para qualquer coisa? Talvez para depuração.

Veja também

Namespace anônimos em C ++

No C ++, convém usar espaços de nome anônimos em vez de estático, o que gera um efeito semelhante, mas oculta ainda mais as definições de tipo: espaços de nome anônimos / anônimos / funções estáticas


39

Depende:

int foo()
{
   static int x;
   return ++x;
}

A função retornaria 1, 2, 3 etc. - a variável não está na pilha.

ac:

static int foo()
{
}

Isso significa que esta função tem escopo apenas neste arquivo. Portanto, ac e bc podem ter foo()s diferentes , e foo não é exposto a objetos compartilhados. Portanto, se você definiu foo em ac, não poderia acessá-lo de b.cou de outros lugares.

Na maioria das bibliotecas C, todas as funções "privadas" são estáticas e a maioria "pública" não é.


18
+1 por mencionar x que não está na pilha ou pilha. Está no espaço estático da memória.
Gob00st

1
@ Gob00st espaço de memória estática? você quis dizer "segmento de dados" ...?
Yousha Aleayoub

24

As pessoas continuam dizendo que 'estático' em C tem dois significados. Ofereço uma maneira alternativa de visualizá-lo, que lhe dá um único significado:

  • A aplicação de 'estático' a um item força esse item a ter duas propriedades: (a) não é visível fora do escopo atual; (b) É persistente.

A razão pela qual parece ter dois significados é que, em C, todo item ao qual 'estático' pode ser aplicado já possui uma dessas duas propriedades , portanto parece que esse uso específico envolve apenas o outro.

Por exemplo, considere variáveis. As variáveis ​​declaradas fora das funções já têm persistência (no segmento de dados), portanto, aplicar 'estático' só pode torná-las visíveis fora do escopo atual (unidade de compilação). Por outro lado, variáveis ​​declaradas dentro de funções já não têm visibilidade fora do escopo atual (função), portanto, aplicar 'estático' só pode torná-las persistentes.

Aplicar 'estático' a funções é como aplicá-lo a variáveis ​​globais - o código é necessariamente persistente (pelo menos no idioma), portanto, apenas a visibilidade pode ser alterada.

NOTA: Esses comentários se aplicam apenas a C. No C ++, aplicar 'estático' aos métodos de classe está realmente dando à palavra-chave um significado diferente. Da mesma forma para a extensão de argumento de matriz C99.


Seu (a) é redundante na melhor das hipóteses. Nenhuma variável é visível fora do seu escopo. Essa é simplesmente a definição de escopo. O que você quer dizer é chamado de ligação no padrão C. staticfornece ligação interna a um identificador.
Jens

16

Da Wikipedia:

Na linguagem de programação C, estática é usada com variáveis ​​e funções globais para definir seu escopo para o arquivo que contém. Nas variáveis ​​locais, estático é usado para armazenar a variável na memória alocada estaticamente, em vez da memória alocada automaticamente. Enquanto o idioma não determina a implementação de qualquer tipo de memória, a memória alocada estaticamente é normalmente reservada no segmento de dados do programa em tempo de compilação, enquanto a memória alocada automaticamente é normalmente implementada como uma pilha de chamadas transitórias.


16

static significa coisas diferentes em diferentes contextos.

  1. Você pode declarar uma variável estática em uma função C. Essa variável é visível apenas na função, no entanto, ela se comporta como um global, pois é inicializada apenas uma vez e mantém seu valor. Neste exemplo, toda vez que você ligar foo(), imprimirá um número crescente. A variável estática é inicializada apenas uma vez.

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
  2. Outro uso de estático é quando você implementa uma função ou variável global em um arquivo .c, mas não deseja que seu símbolo seja visível fora do .objgerado pelo arquivo. por exemplo

    static void foo() { ... }

8

Se você declarar uma variável em uma função estática, seu valor não será armazenado na pilha de chamadas de funções e continuará disponível quando você chamar a função novamente.

Se você declarar uma variável global estática, seu escopo será restrito ao arquivo em que você a declarou. Isso é um pouco mais seguro que um global comum, que pode ser lido e modificado durante todo o programa.


8

Detesto responder a uma pergunta antiga, mas acho que ninguém mencionou como a K&R a explica na seção A4.1 de "A linguagem de programação C".

Em resumo, a palavra estática é usada com dois significados:

  1. Static é uma das duas classes de armazenamento (a outra é automática). Um objeto estático mantém seu valor entre invocações. Os objetos declarados fora de todos os blocos são sempre estáticos e não podem ser automatizados.
  2. Porém, quando a static palavra - chave (grande ênfase em ser usada no código como palavra-chave) é usada com uma declaração, ela fornece a ligação interna ao objeto para que ela possa ser usada apenas dentro dessa unidade de tradução. Mas se a palavra-chave for usada em uma função, ela altera a classe de armazenamento do objeto (o objeto só seria visível dentro dessa função). O oposto de static é a externpalavra - chave, que fornece a ligação externa a um objeto.

Peter Van Der Linden dá esses dois significados em "Programação C Especialista":

  • Dentro de uma função, mantém seu valor entre as chamadas.
  • No nível da função, visível apenas neste arquivo.

Há uma terceira classe de armazenamento, registre-se . Algumas pessoas também defendem uma quarta classe de armazenamento, alocada , para o armazenamento retornado por malloc e amigos.
Jens

@Jens 'register' é apenas uma dica para o compilador; o armazenamento do registro não pode ser imposto de dentro da fonte C. Portanto, eu não consideraria uma classe de armazenamento.
GermanNerd

1
@GermanNerd Acho que o padrão ISO C discorda de sua visão, pois claramente faz registerum especificador de classe de armazenamento (C99 6.7.1 Especificadores de classe de armazenamento). E é mais do que apenas uma dica, por exemplo, você não pode aplicar o endereço do operador &em um objeto com classe de armazenamento, registerindependentemente de o compilador alocar um registro ou não.
Jens

@ Jens Obrigado por me lembrar sobre &. Eu poderia ter feito muito C ++ ..... De qualquer forma, enquanto 'register' é um especificador de classe de armazenamento, na realidade o compilador provavelmente criará o mesmo código de máquina para o especificador 'auto' (inútil) e para o 'register 'especificador. Portanto, a única coisa que resta é a restrição no nível do código-fonte de não ser possível obter um endereço. Aliás, essa pequena discussão me levou a encontrar um bug no Netbeans; desde a minha última atualização, o padrão é a cadeia de ferramentas g ++ em novos projetos C!
GermanNerd

6

Em C, estática tem dois significados, dependendo do escopo de seu uso. No escopo global, quando um objeto é declarado no nível do arquivo, significa que esse objeto é visível apenas dentro desse arquivo.

Em qualquer outro escopo, ele declara um objeto que manterá seu valor entre os diferentes momentos em que o escopo específico é inserido. Por exemplo, se um int for delcared dentro de um procedimento:

void procedure(void)
{
   static int i = 0;

   i++;
}

o valor de 'i' é inicializado como zero na primeira chamada para o procedimento e o valor é retido cada vez que o procedimento é chamado. se 'i' fosse impresso, produziria uma sequência de 0, 1, 2, 3, ...


5

É importante observar que variáveis ​​estáticas nas funções são inicializadas na primeira entrada dessa função e persistem mesmo após o término da chamada; no caso de funções recursivas, a variável estática é inicializada apenas uma vez e persiste também em todas as chamadas recursivas e mesmo após o término da chamada da função.

Se a variável foi criada fora de uma função, significa que o programador só pode usar a variável no arquivo de origem em que a variável foi declarada.


5

Se você declarar isso em um mytest.carquivo:

static int my_variable;

Então essa variável pode ser vista apenas neste arquivo. A variável não pode ser exportada para nenhum outro lugar.

Se você declarar dentro de uma função, o valor da variável manterá seu valor sempre que a função for chamada.

Uma função estática não pode ser exportada de fora do arquivo. Portanto, em um *.carquivo, você está ocultando as funções e as variáveis ​​se as declarar estáticas.


4

Variáveis ​​estáticas em C têm a vida útil do programa.

Se definidos em uma função, eles têm escopo local, ou seja, podem ser acessados ​​apenas dentro dessas funções. O valor das variáveis ​​estáticas é preservado entre as chamadas de função.

Por exemplo:

void function()
{
    static int var = 1;
    var++;
    printf("%d", var);
}

int main()
{
    function(); // Call 1
    function(); // Call 2
}

No programa acima, var é armazenado no segmento de dados. Sua vida útil é todo o programa C.

Após a chamada de função 1, vartorna-se 2. Após a chamada de função 2, vartorna-se 3.

O valor de varnão é destruído entre as chamadas de funções.

Se vartivesse entre variáveis ​​não estáticas e locais, seria armazenado no segmento de pilha no programa C. Como o quadro da pilha da função é destruído após o retorno da função, o valor de vartambém é destruído.

Variáveis ​​estáticas inicializadas são armazenadas no segmento de dados do programa C, enquanto variáveis ​​não inicializadas são armazenadas no segmento BSS.

Outra informação sobre estática: se uma variável é global e estática, ela possui o tempo de vida do programa C, mas o escopo do arquivo. É visível apenas nesse arquivo.

Para tentar isso:

file1.c

static int x;

int main()
{
    printf("Accessing in same file%d", x):
}

file2.c

    extern int x;
    func()
    {
        printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
    }

run gcc -c file1.c

gcc -c file2.c

Agora tente vinculá-los usando:

gcc -o output file1.o file2.o

Isso causaria um erro de vinculador, pois x tem o escopo do arquivo file1.c e o vinculador não conseguiria resolver a referência à variável x usada no arquivo2.c.

Referências:

  1. http://en.wikipedia.org/wiki/Translation_unit_(programming)
  2. http://en.wikipedia.org/wiki/Call_stack

Eu entendo que os dados são persistentes, o que significa que não será perdido após cada chamada de função, mas por que não static int var = 1;mudar o valor de volta para uma de cada vez
Eames

3

Uma variável estática é uma variável especial que você pode usar em uma função e salva os dados entre as chamadas e não os exclui entre as chamadas. Por exemplo:

void func(){
    static int count; // If you don't declare its value, the value automatically initializes to zero
    printf("%d, ", count);
    ++count;
}

void main(){
    while(true){
        func();
    }
}

A saída:

0, 1, 2, 3, 4, 5, ...


Você pode substituir printf("%d, ", count); count++;por `printf ("% d, ", count ++) (não que isso importe: P).
RastaJedi

2

Um valor de variável estática persiste entre diferentes chamadas de função e seu escopo é limitado ao bloco local, uma var estática sempre inicializa com o valor 0


2

Existem 2 casos:

(1) Variáveis ​​locais declaradas static: alocadas no segmento de dados em vez da pilha. Seu valor é retido quando você chama a função novamente.

(2) Variáveis ​​ou funções globais declaradas static: Unidade de compilação externa invisível (ou seja, são símbolos locais na tabela de símbolos durante o vínculo).


1

As variáveis ​​estáticas têm a propriedade de preservar seu valor mesmo depois de estarem fora do escopo! Portanto, as variáveis ​​estáticas preservam o valor anterior no escopo anterior e não são inicializadas novamente no novo escopo.

Veja isso por exemplo - Uma variável int estática permanece na memória enquanto o programa está sendo executado. Uma variável normal ou automática é destruída quando uma chamada de função em que a variável foi declarada termina.

#include<stdio.h> 
int fun() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("%d ", fun()); 
  printf("%d ", fun()); 
  return 0; 
}

Isso produzirá: 1 2

Como 1 permanece na memória como foi declarado estático

Variáveis ​​estáticas (como variáveis ​​globais) são inicializadas como 0 se não forem inicializadas explicitamente. Por exemplo, no programa abaixo, o valor de x é impresso como 0, enquanto o valor de y é algo como lixo. Veja isso para mais detalhes.

#include <stdio.h> 
int main() 
{ 
    static int x; 
    int y; 
    printf("%d \n %d", x, y); 
}

Isso produzirá: 0 [some_garbage_value]

Estes são os principais que descobri que não foram explicados acima para um novato!


-1

Na programação C, staticé uma palavra-chave reservada que controla a vida útil e a visibilidade. Se declararmos uma variável como estática dentro de uma função, ela será visível apenas em toda a função. Nesse uso, a vida útil dessa variável estática será iniciada quando uma chamada de função e será destruída após a execução dessa função. você pode ver o seguinte exemplo:

#include<stdio.h> 
int counterFunction() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("First Counter Output = %d\n", counterFunction()); 
  printf("Second Counter Output = %d ", counterFunction()); 
  return 0; 
}

O programa acima nos dará esta saída:

First Counter Output = 1 
Second Counter Output = 1 

Porque assim que chamarmos a função, ela inicializará o count = 0. E enquanto executamos o, counterFunctionele destruirá a variável count.


2
> O programa acima nos dará esta saída: Primeira saída do contador = 1 Segunda saída do contador = 1 <Não é verdade. Variáveis ​​estáticas são inicializadas apenas uma vez. Portanto, a saída será 1, depois 2 e assim por diante.
GermanNerd
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.