Variable estática dentro de una función en C


119

¿Qué se imprimirá? 6 6 o 6 7? ¿Y por qué?

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

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

54
¿Cuál es el problema a intentar?
Andrew

12
¿Intentaste escribir esto y comprobarlo por ti mismo?
wilhelmtell

21
Quiero entender por qué.
Vadiklk

7
@Vadiklk, así que haz una pregunta que comience con "Por qué"
Andrey

1
ideone.com/t9Bbe ¿Qué esperaría? ¿El resultado no coincide con sus expectativas? ¿Por qué esperabas tu resultado?
eckes

Respuestas:


187

Aquí hay dos cuestiones, duración y alcance.

El alcance de la variable es donde se puede ver el nombre de la variable. Aquí, x es visible solo dentro de la función foo ().

La vida útil de una variable es el período durante el cual existe. Si x se definiera sin la palabra clave static, el tiempo de vida sería desde la entrada en foo () hasta el retorno de foo (); por lo que se reinicializaría a 5 en cada llamada.

La palabra clave static actúa para extender la vida útil de una variable a la vida útil del programa; por ejemplo, la inicialización ocurre una vez y solo una vez y luego la variable retiene su valor, sea lo que sea que haya llegado a ser, en todas las llamadas futuras a foo ().


15
@devanl, sí lo somos.
orion elenzil

1
Simple y lógico :)
Dimitar Vukman

¿En qué escenarios necesitamos declarar una variable como estática dentro de una función ?, ¿solo tengo curiosidad por saber ya que no he usado esto antes?
Akay

Yo diría gracias, pero todo esto fue respondido en la parte superior de la página. Me hace reír que la gente no solo ejecute su propio código. xD
Charco

Esta respuesta es incorrecta. En el momento en que piensa en funciones recursivas, las definiciones que se describen aquí no explican el comportamiento.
Philip Couling

53

Salida : 6 7

Motivo : la variable estática se inicializa solo una vez (a diferencia de la variable automática) y la definición adicional de variable estática se omitirá durante el tiempo de ejecución. Y si no se inicializa manualmente, se inicializa automáticamente con el valor 0. Entonces,

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;
}

10

6 7

El compilador organiza que la inicialización de la variable estática no ocurra cada vez que se ingresa la función


10

Eso es lo mismo que tener el siguiente programa:

static int x = 5;

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

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

Todo lo que hace la palabra clave estática en ese programa es decirle al compilador (esencialmente) 'hey, tengo una variable aquí a la que no quiero que nadie más acceda, no le digas a nadie más que existe'.

Dentro de un método, la palabra clave estática le dice al compilador lo mismo que antes, pero también, 'no le digas a nadie que esto existe fuera de esta función, solo debería ser accesible dentro de esta función'.

espero que esto ayude


13
Bueno, en realidad no es lo mismo. Todavía existe el problema del alcance en X. En este ejemplo, podría empujar y poner xin main; es global. En el ejemplo original xera local para foo, solo visible dentro de ese bloque, lo que generalmente es preferible: si foo existe para mantener xde manera predecible y visible, entonces dejar que otros lo pinchen es generalmente peligroso. Como otro beneficio de mantenerlo dentro del alcance foo() , también se mantiene foo()portátil.
user2149140

2
@ user2149140 'no le diga a nadie que esto existe fuera de esta función, solo debería ser accesible dentro de esta función'
DCShannon

3
Si bien ha abordado el problema del alcance debido al lugar donde se declara la variable, la descripción de estática como alcance que afecta, en lugar de duración, parece incorrecta.
DCShannon

1
@Chameleon La pregunta está etiquetada como c, por lo que en este contexto, su ejemplo sería ilegal a nivel global. (C requiere inicializadores constantes para globales, C ++ no).
Richard J. Ross III

5

Una variable estática dentro de una función tiene una vida útil siempre que se ejecute su programa. No se asignará cada vez que se llame a su función y se desasignará cuando su función regrese.


Decir esto es como una variable "global" y luego decir EXCEPTO que no puedes acceder a ella es un oxímoron. Global significa accesible desde cualquier lugar. Que en este caso de una función estática DENTRO de una función NO es accesible en todas partes. El problema en OP, como han señalado otros, es sobre el alcance y la vida útil. Por favor, no confunda a la gente con el uso del término "global" y los engañe sobre el alcance de la variable.
ChuckB

@ChuckB: Correcto. Arreglado. Bueno, han pasado 6 años. ¡Mi respuesta anterior tenía la percepción de hace 6 años!
Donotalo

5

Salida: 6,7

Razón

La declaración de xestá dentro foopero la x=5inicialización tiene lugar fuera de foo!

Lo que necesitamos entender aquí es que

static int x = 5;

no es lo mismo que

static int x;
x = 5;

Otras respuestas han utilizado las palabras importantes aquí, alcance y vida útil, y señalaron que el alcance de xes desde el punto de su declaración en la función foohasta el final de la función foo. Por ejemplo, verifiqué moviendo la declaración al final de la función, y eso hace que xno se declare en elx++; declaración.

Entonces, la parte static int x(alcance) de la declaración realmente se aplica donde la lee, en algún lugar DENTRO la función y solo de allí en adelante, no arriba dentro de la función.

Sin embargo, la parte x = 5(de por vida) de la declaración es la inicialización de la variable y sucede FUERA de la función como parte de la carga del programa. La variable xnace con un valor de 5cuando se carga el programa.

Leí esto en uno de los comentarios: " Además, esto no aborda la parte realmente confusa, que es el hecho de que el inicializador se omite en llamadas posteriores " . Se omite en todas las llamadas. La inicialización de la variable está fuera del código de función propiamente dicho.

El valor de 5 se establece teóricamente independientemente de si se llama a foo o no, aunque un compilador podría optimizar la función si no la llama en ningún lado. El valor de 5 debe estar en la variable antes de que se llame a foo.

Dentro de foo, la declaraciónstatic int x = 5; es poco probable que genere algún código.

Encontré los xusos de la dirección cuando puse una función fooen un programa mío, y luego (correctamente) adiviné que se usaría la misma ubicación si ejecutaba el programa nuevamente. La captura de pantalla parcial a continuación muestra que xtiene el valor 5incluso antes de la primera llamada a foo.

Break Point antes de la primera llamada a foo


2

La salida será 6 7. Una variable estática (ya sea dentro de una función o no) se inicializa exactamente una vez, antes de que se ejecute cualquier función en esa unidad de traducción. Después de eso, conserva su valor hasta que se modifica.


1
¿Está seguro de que la estática se inicializa antes de llamar a la función y no en la primera llamada de la función?
Jesse Pepper

@JessePepper: Al menos si la memoria falla, esto depende de si estás hablando de C ++ 98/03 o C ++ 11. En C ++ 98/03, creo que es como se describió anteriormente. En C ++ 11, el subproceso hace que eso sea esencialmente imposible de hacer, por lo que la inicialización se realiza en la primera entrada a la función.
Jerry Coffin

2
Creo que estás equivocado en realidad. Creo que incluso antes de C ++ 11 solo se inicializó cuando se llamó a la función. Esto es importante para una solución común al problema de dependencia de la inicialización estática.
Jesse Pepper

2

Vadiklk,

Por qué ...? La razón es que la variable estática se inicializa solo una vez y mantiene su valor durante todo el programa. significa que puede usar variables estáticas entre llamadas a funciones. también se puede utilizar para contar "cuántas veces se llama a una función"

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

y la respuesta es 5 4 3 2 1 y no 5 5 5 5 5 5 .... (ciclo infinito) como esperabas. de nuevo, la razón por la que la variable estática se inicializa una vez, la próxima vez que se llame a main () no se inicializará en 5 porque ya está inicializada en el programa. Por lo tanto, podemos cambiar el valor pero no se puede reinicializar. Así es como funciona la variable estática.

o puede considerarlo por almacenamiento: las variables estáticas se almacenan en la sección de datos de un programa y las variables que se almacenan en la sección de datos se inicializan una vez. y antes de la inicialización se guardan en la sección BSS.

A su vez, las variables Auto (locales) se almacenan en la pila y todas las variables en la pila se reinicializan todo el tiempo cuando se llama a la función, ya que se crea un nuevo FAR (registro de activación de función) para eso.

Está bien para una mayor comprensión, haga el ejemplo anterior sin "estática" y déjele saber cuál será el resultado. Eso te hace comprender la diferencia entre estos dos.

Gracias Javed


1

Leamos el artículo de Wikipedia sobre variables estáticas ...

Variables locales estáticas: las variables declaradas como estáticas dentro de una función se asignan estáticamente y tienen el mismo alcance que las variables locales automáticas. Por lo tanto, cualquier valor que la función ponga en sus variables locales estáticas durante una llamada seguirá estando presente cuando se vuelva a llamar a la función.


5
¡Eso es terrible! "las variables declaradas como estáticas dentro de una función se asignan estáticamente" - ¡no explica nada, a menos que ya sepa lo que significa!

@Blank: bueno, para eso pensé que era la segunda oración. Aunque supongo que tienes razón, debería estar mejor redactado.
Andrew White

Además, esto no aborda la parte realmente confusa, que es el hecho de que el inicializador se omite en llamadas posteriores.
Tom Auger

asignado estáticamente significa que no hay pila ni montón.
Camaleón

1

Se imprimirá 6 7 como, como se prueba fácilmente, y esta es la razón: foo se llama por primera vez, la variable estática x se inicializa en 5. Luego se incrementa a 6 y se imprime.

Ahora para la próxima llamada a foo. El programa omite la inicialización de la variable estática y, en su lugar, utiliza el valor 6 que se asignó ax la última vez. La ejecución procede con normalidad, dándole el valor 7.


1
6 7

x es una variable global que solo es visible desde foo (). 5 es su valor inicial, almacenado en la sección .data del código. Cualquier modificación posterior sobrescribe el valor anterior. No hay ningún código de asignación generado en el cuerpo de la función.


1

6 y 7 Debido a que la variable estática se inicia solo una vez, 5 ++ se convierte en 6 en la primera llamada 6 ++ se convierte en 7 en la segunda llamada Nota: cuando se produce la segunda llamada, el valor de x es 6 en lugar de 5 porque x es una variable estática.


0

En C ++ 11 al menos, cuando la expresión utilizada para inicializar una variable estática local no es un 'constexpr' (no puede ser evaluada por el compilador), la inicialización debe ocurrir durante la primera llamada a la función. El ejemplo más simple es usar directamente un parámetro para inicializar la variable estática local. Por lo tanto, el compilador debe emitir código para adivinar si la llamada es la primera o no, lo que a su vez requiere una variable booleana local. He compilado tal ejemplo y verifiqué que esto es cierto al ver el código ensamblador. El ejemplo puede ser así:

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

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

por supuesto, cuando la expresión es 'constexpr', entonces esto no es necesario y la variable se puede inicializar al cargar el programa utilizando un valor almacenado por el compilador en el código ensamblador de salida.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.