Variável estática dentro de uma função em C

119

O que será impresso? 6 6 ou 6 7? E porque?

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}
Vadiklk
fonte
54
Qual é o problema para tentar?
Andrew
12
Você tentou digitar isso e ver por si mesmo?
wilhelmtell
20
Eu quero entender o porquê.
Vadiklk
7
@Vadiklk então faça perguntas começando com "Por quê"
Andrey
1
ideone.com/t9Bbe O que você esperava? O resultado não corresponde à sua expectativa? Por que você esperava seu resultado?
eckes

Respostas:

186

Existem duas questões aqui, vida útil e escopo.

O escopo da variável é onde o nome da variável pode ser visto. Aqui, x é visível apenas dentro da função foo ().

O tempo de vida de uma variável é o período durante o qual ela existe. Se x fosse definido sem a palavra-chave static, o tempo de vida seria da entrada em foo () até o retorno de foo (); então ele seria reinicializado para 5 em cada chamada.

A palavra-chave static atua para estender o tempo de vida de uma variável para o tempo de vida do programa; por exemplo, a inicialização ocorre apenas uma vez e então a variável retém seu valor - seja lá o que for - em todas as chamadas futuras para foo ().


fonte
15
@devanl, sim, nós somos.
orion elenzil
1
Simples e lógico :)
Dimitar Vukman
em quais cenários precisamos declarar uma variável como estática dentro de uma função ?, apenas curioso para saber como eu não usei isso antes?
Akay
Eu diria obrigado, mas tudo isso foi respondido no topo da página. me faz rir que as pessoas não executam apenas seus próprios códigos. xD
Puddle
Essa resposta está errada. No momento em que você pensa sobre funções recursivas, as definições descritas aqui não explicam o comportamento!
Philip Couling
52

Produto : 6 7

Motivo : a variável estática é inicializada apenas uma vez (ao contrário da variável automática) e a definição posterior da variável estática seria ignorada durante o tempo de execução. E se não for inicializado manualmente, é inicializado pelo valor 0 automaticamente. Assim,

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}
Nitesh Borad
fonte
10

6 7

o compilador faz com que a inicialização da variável estática não aconteça cada vez que a função é inserida

Chaim Geretz
fonte
10

Isso é o mesmo que ter o seguinte programa:

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

Tudo o que a palavra-chave estática faz nesse programa é dizer ao compilador (essencialmente) 'ei, eu tenho uma variável aqui que não quero que ninguém mais acesse, não diga a ninguém que ela existe'.

Dentro de um método, a palavra-chave estática diz ao compilador o mesmo que acima, mas também, 'não diga a ninguém que isso existe fora desta função, só deve estar acessível dentro desta função'.

Eu espero que isso ajude

Richard J. Ross III
fonte
13
Bem, na verdade não é o mesmo. Ainda há o problema de escopo em X. Neste exemplo, você poderia cutucar e atacar com xno principal; é global. No exemplo original, xera local para foo, visível apenas enquanto dentro desse bloco, o que geralmente é preferível: se foo existe para manter de xmaneiras previsíveis e visíveis, então deixar que outros cutuquem é geralmente perigoso. Como outro benefício de mantê-lo dentro do escopo, foo() ele também é foo()portátil.
user2149140
2
@ user2149140 'não diga a ninguém que isso existe fora desta função, só deve estar acessível dentro desta função'
DCShannon 01 de
3
Embora você tenha abordado a questão do escopo devido a onde a variável é declarada, a descrição de estática como afetando o escopo, em vez do tempo de vida, parece incorreta.
DCShannon 01 de
1
@Chameleon A questão está marcada como c, portanto, neste contexto, seu exemplo seria ilegal em âmbito global. (C requer inicializadores constantes para globais, C ++ não).
Richard J. Ross III
5

Uma variável estática dentro de uma função tem uma vida útil enquanto seu programa é executado. Ele não será alocado sempre que sua função for chamada e desalocada quando sua função retornar.

Donotalo
fonte
Dizer que isso é como uma variável "global" e, em seguida, dizer EXCETO que você não pode acessá-la é um oxímoro. Global significa acessível em qualquer lugar. Que neste caso de uma função estática DENTRO de uma função NÃO está acessível em todos os lugares. A questão no OP, como outros observaram, é sobre escopo e tempo de vida. Por favor, não confunda as pessoas com o uso do termo 'global' e enganando-as sobre o escopo da variável.
ChuckB
@ChuckB: Correto. Corrigido. Bem, já se passaram 6 anos. Minha resposta anterior teve a percepção de 6 anos atrás!
Donotalo,
5

Produto: 6,7

Razão

A declaração de xestá dentro, foomas a x=5inicialização ocorre fora de foo!

O que precisamos entender aqui é que

static int x = 5;

não é o mesmo que

static int x;
x = 5;

Outras respostas usaram as palavras importantes aqui, escopo e tempo de vida, e apontaram que o escopo de xé do ponto de sua declaração na função fooaté o fim da função foo. Por exemplo, eu verifiquei movendo a declaração para o final da função, e isso torna xnão declarado na x++;declaração.

Portanto, a parte static int x(escopo) da instrução realmente se aplica onde você a lê, em algum lugar DENTRO da função e apenas a partir daí, não acima dela dentro da função.

No entanto, a parte x = 5(vitalícia) da instrução é a inicialização da variável e ocorrendo FORA da função como parte do carregamento do programa. A variável xnasce com um valor de5 quando o programa é carregado.

Eu li isso em um dos comentários: " Além disso, isso não resolve a parte realmente confusa, que é o fato de que o inicializador é ignorado nas chamadas subsequentes. " Ele é ignorado em todas as chamadas. A inicialização da variável está fora do código de função adequado.

O valor de 5 é teoricamente definido independentemente de foo ser ou não chamado, embora um compilador possa otimizar a função se você não a chamar em qualquer lugar. O valor 5 deve estar na variável antes de foo ser chamado.

Dentro de foo, a declaraçãostatic int x = 5; é improvável que gere qualquer código.

Eu descobri o endereço xusado quando coloquei uma função fooem um programa meu e então (corretamente) adivinhei que o mesmo local seria usado se eu executasse o programa novamente. A captura de tela parcial abaixo mostra que xtem o valor 5mesmo antes da primeira chamada para foo.

Break Point antes da primeira chamada para foo

Ivan
fonte
2

A saída será 6 7 . Uma variável estática (dentro de uma função ou não) é inicializada exatamente uma vez, antes de qualquer função nessa unidade de tradução ser executada. Depois disso, ele mantém seu valor até ser modificado.

Jerry Coffin
fonte
1
Tem certeza de que a estática é inicializada antes de a função ser chamada, e não na primeira chamada da função?
Jesse Pepper
@JessePepper: Pelo menos se falta memória, isso depende se você está falando sobre C ++ 98/03 ou C ++ 11. Em C ++ 98/03, acredito que seja conforme descrito acima. No C ++ 11, o threading torna isso essencialmente impossível de fazer, então a inicialização é feita na primeira entrada na função.
Jerry Coffin
2
Acho que você está errado, na verdade. Acho que mesmo antes do C ++ 11 ele só foi inicializado quando a função é chamada. Isso é importante para uma solução comum para o problema de dependência de inicialização estática.
Jesse Pepper
2

Vadiklk,

Por quê ...? A razão é que a variável estática é inicializada apenas uma vez e mantém seu valor em todo o programa. significa que você pode usar a variável estática entre as chamadas de função. também pode ser usado para contar "quantas vezes uma função é chamada"

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

e a resposta é 5 4 3 2 1 e não 5 5 5 5 5 5 .... (loop infinito) como você está esperando. novamente, o motivo pelo qual a variável estática é inicializada uma vez, na próxima vez que main () for chamado, ela não será inicializada em 5 porque já foi inicializada no programa. Portanto, podemos alterar o valor, mas não podemos reinicializar. É assim que funciona a variável estática.

ou você pode considerar como por armazenamento: variáveis ​​estáticas são armazenadas na seção de dados de um programa e variáveis ​​que são armazenadas na seção de dados são inicializadas uma vez. e antes da inicialização, eles são mantidos na seção BSS.

Por sua vez, as variáveis ​​Auto (locais) são armazenadas na pilha e todas as variáveis ​​na pilha são reinicializadas o tempo todo quando a função é chamada, pois um novo FAR (registro de ativação da função) é criado para isso.

ok para mais compreensão, faça o exemplo acima sem "estático" e deixe você saber qual será o resultado. Isso faz você entender a diferença entre os dois.

Obrigado Javed

Javed
fonte
1

Vamos apenas ler o artigo da Wikipedia sobre variáveis ​​estáticas ...

Variáveis ​​locais estáticas: as variáveis ​​declaradas como estáticas dentro de uma função são alocadas estaticamente, embora tenham o mesmo escopo das variáveis ​​locais automáticas. Portanto, quaisquer valores que a função coloque em suas variáveis ​​locais estáticas durante uma chamada ainda estarão presentes quando a função for chamada novamente.

Andrew White
fonte
5
Isso é terrível! "variáveis ​​declaradas como estáticas dentro de uma função são alocadas estaticamente" - isso não explica nada, a menos que você já saiba o que significa!
@Blank: bem, pensei que era para isso que servia a segunda frase. Embora eu ache que você está certo, deveria ser mais bem formulado.
Andrew White
Além disso, isso não resolve a parte realmente confusa, que é o fato de que o inicializador é ignorado nas chamadas subsequentes.
Tom Auger
alocado estaticamente significa nenhuma pilha, nem heap.
Camaleão de
1

Você obterá 6 7 impresso como, como é facilmente testado, e aqui está o motivo: Quando fooé chamada pela primeira vez, a variável estática x é inicializada para 5. Em seguida, é incrementada para 6 e impressa.

Agora para a próxima chamada para foo. O programa pula a inicialização da variável estática e, em vez disso, usa o valor 6 que foi atribuído ax da última vez. A execução prossegue normalmente, fornecendo o valor 7.

Ken Wayne VanderLinde
fonte
1
6 7

x é uma variável global visível apenas a partir de foo (). 5 é seu valor inicial, conforme armazenado na seção .data do código. Qualquer modificação subsequente sobrescreverá o valor anterior. Não há código de atribuição gerado no corpo da função.

mouviciel
fonte
1

6 e 7 Como a variável estática inicializa apenas uma vez, So 5 ++ torna-se 6 na 1ª chamada 6 ++ torna-se 7 na 2ª chamada Nota - quando ocorre a 2ª chamada, o valor de x é 6 em vez de 5 porque x é a variável estática.

Tushar Shirsath
fonte
0

No C ++ 11, pelo menos, quando a expressão usada para inicializar uma variável estática local não é um 'constexpr' (não pode ser avaliada pelo compilador), então a inicialização deve acontecer durante a primeira chamada para a função. O exemplo mais simples é usar diretamente um parâmetro para inicializar a variável estática local. Portanto, o compilador deve emitir código para adivinhar se a chamada é a primeira ou não, o que, por sua vez, requer uma variável booleana local. Compilei esse exemplo e verifiquei se isso é verdade, vendo o código do assembly. O exemplo pode ser assim:

void f( int p )
{
  static const int first_p = p ;
  cout << "first p == " << p << endl ;
}

void main()
{
   f(1); f(2); f(3);
}

é claro, quando a expressão é 'constexpr', então isso não é necessário e a variável pode ser inicializada no carregamento do programa usando um valor armazenado pelo compilador no código assembly de saída.

user5122888
fonte