¿Qué significa "estático" en C?


1140

He visto la palabra staticusada en diferentes lugares en el código C; ¿Es esto como una función / clase estática en C # (donde la implementación se comparte entre objetos)?



15
¿Cuál es la razón para eliminar "en un programa C" al final del título, @Lundin? Es ligeramente redundante en presencia de la etiqueta c , pero me permite ver la categorización más rápidamente, sin inspeccionar las etiquetas. Esta redundancia es muy cómoda cuando llego a la pregunta desde una dirección que también puede contener preguntas sobre otros idiomas, por ejemplo, búsqueda estática o de Google.
Palec

55
@Palec Existe una política SO de que los elementos que están presentes en la lista de etiquetas son redundantes en el título. El sitio agregará automáticamente C al sitio web real. Un Google para "C estática" da esta respuesta como el mejor éxito. La razón por la que esto se modificó es porque esta pregunta ahora forma parte de las preguntas frecuentes del lenguaje SO C y todas las publicaciones que se agregan se pulen un poco.
Lundin

1
@Lundin Prefiero mantener "C" en el título, porque SO solo agrega una etiqueta al título (¿la más común?). ¿Qué pasa si algún día la "sintaxis" llega a más preguntas que C (ya que es una cuestión de lenguaje cruzado)? Prefiero usar el comportamiento explícito :-) Editar: ah, pero hay una meta pregunta que dice lo contrario: meta.stackexchange.com/questions/19190/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
Esta es una explicación que encontré en Quora. Definitivamente vale la pena leer!
nalzok

Respuestas:


1521
  1. Una variable estática dentro de una función mantiene su valor entre invocaciones.
  2. Una variable global estática o una función se "ve" solo en el archivo en el que se declara

(1) es el tema más extraño si eres un novato, así que aquí hay un ejemplo:

#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();
}

Esto 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

Esto es útil para los casos en que una función necesita mantener cierto estado entre invocaciones y no desea utilizar variables globales. Sin embargo, tenga cuidado, esta característica debe usarse con moderación: hace que su código no sea seguro para subprocesos y sea más difícil de entender.

(2) Se utiliza ampliamente como una función de "control de acceso". Si tiene un archivo .c que implementa alguna funcionalidad, generalmente expone solo unas pocas funciones "públicas" a los usuarios. El resto de sus funciones deben realizarse staticpara que el usuario no pueda acceder a ellas. Esto es encapsulación, una buena práctica.

Citando Wikipedia :

En el lenguaje de programación C, static se usa con variables y funciones globales para establecer su alcance en el archivo contenedor. En las variables locales, static se usa para almacenar la variable en la memoria asignada estáticamente en lugar de la memoria asignada automáticamente. Si bien el lenguaje no dicta la implementación de ninguno de los tipos de memoria, la memoria asignada estáticamente se reserva generalmente en el segmento de datos del programa en tiempo de compilación, mientras que la memoria asignada automáticamente se implementa normalmente como una pila de llamadas transitorias.

Y para responder a su segunda pregunta, no es como en C #.

En C ++, sin embargo, statictambién se usa para definir atributos de clase (compartidos entre todos los objetos de la misma clase) y métodos. En C no hay clases, por lo que esta característica es irrelevante.


179
Pax, el OP no sabe sobre estática, ¿así que sugieres sumergirlo en la diferencia entre unidades de compilación y archivos? :-)
Eli Bendersky

138
Una unidad de compilación es un archivo único que ve el compilador. Su archivo .c puede incluir otros archivos .c, pero después de que el preprocesador clasifique las inclusiones, el compilador finalmente ve una sola "unidad de compilación".
Eli Bendersky

81
@robUK: el compilador ni siquiera conoce los archivos .h; estos se combinan en los archivos .c del preprocesador. Entonces sí, puede decir que el archivo .c, con todos los encabezados incluidos en él, es una sola unidad de compilación.
Eli Bendersky

66
@TonyD quizás sea confuso, pero así es como funciona la compilación. Por lo general, puede ser uno .cy un montón de archivos de encabezado, pero el demonio siempre está en lo que no es típico.
Peter

77
@TonyD El compilador hace la compilación. El preprocesador realiza el preprocesamiento. Llamar a la cadena de herramientas 'el compilador' no cambia lo que es o lo que hace.
Miles Rout

231

Hay un uso más no cubierto aquí, y es como parte de una declaración de tipo de matriz como argumento para una función:

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

En este contexto, esto especifica que los argumentos pasados ​​a esta función deben ser una matriz de tipo charcon al menos 10 elementos. Para más información vea mi pregunta aquí .


3
¿No pensé que C tenía argumentos de matriz? Linus Torvalds se enoja con la gente que hace esto.
suprjami

13
@jamieb: C no tiene argumentos de matriz, pero esto significa específicos de sintaxis que las Espera de función arg[0]a través de a arg[9]tener valores (que también implica que la función no acepta un puntero null). Los compiladores podrían utilizar esta información de alguna manera para la optimización, y los analizadores estáticos pueden utilizar esta información para garantizar que la función nunca reciba un puntero nulo (o si puede decirlo, una matriz con menos elementos de los especificados).
dreamlax

19
@Qix - Este fue un nuevo significado sobrecargado dado staticen C99. Tiene más de una década y media de antigüedad, pero no todos los escritores de compiladores han adoptado todas las características de C99, por lo que C99 en su conjunto sigue siendo desconocido.
Happy Green Kid Naps

@suprjami No estoy 100% seguro de lo que quiere decir con "argumentos de matriz" , pero si quiere decir int arr[n];, entonces es un VLA (matriz de longitud variable) , que se agregó en C99. ¿Es eso lo que querías decir?
RastaJedi

170

Respuesta corta ... depende.

  1. Las variables locales definidas estáticamente no pierden su valor entre las llamadas a funciones. En otras palabras, son variables globales, pero abarcan la función local en la que se definen.

  2. Las variables globales estáticas no son visibles fuera del archivo C en el que están definidas.

  3. Las funciones estáticas no son visibles fuera del archivo C en el que están definidas.


8
Entonces, ¿"función estática" y "función privada" significan lo mismo? De manera similar, ¿son "variables globales estáticas" y "variables globales privadas" lo mismo?
user1599964

40
Esto es sobre C. No hay público / privado en C.
Chris

19
@ user1599964 aunque no existe privateen C, su analogía es buena: la estática hace que las cosas sean "privadas" para un archivo dado. Y los archivos en C a menudo se asignan a clases en C ++.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

67

Ejemplo de alcance variable de múltiples archivos

Aquí ilustramos cómo la estática afecta el alcance de las definiciones de funciones en múltiples archivos.

C.A

#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("");
}

C Principal

#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 aguas arriba .

Compilar y ejecutar:

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

Salida:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

Interpretación

  • hay dos variables separadas para si, una para cada archivo
  • hay una sola variable compartida para i

Como de costumbre, cuanto menor sea el alcance, mejor, así que siempre declare variables staticsi puede.

En la programación en C, los archivos se usan a menudo para representar "clases", y las staticvariables representan miembros privados estáticos de la clase.

¿Qué dicen las normas al respecto?

C99 N1256 draft 6.7.1 "Especificadores de clase de almacenamiento" dice que statices un "especificador de clase de almacenamiento".

6.2.2 / 3 "Vínculos de identificadores" dice staticimplica internal linkage:

Si la declaración de un identificador de alcance de archivo para un objeto o una función contiene el especificador de clase de almacenamiento estático, el identificador tiene un enlace interno.

y 6.2.2 / 2 dice que se internal linkagecomporta como en nuestro ejemplo:

En el conjunto de unidades de traducción y bibliotecas que constituyen un programa completo, cada declaración de un identificador particular con enlace externo denota el mismo objeto o función. Dentro de una unidad de traducción, cada declaración de un identificador con enlace interno denota el mismo objeto o función.

donde "la unidad de traducción es un archivo fuente después del preprocesamiento.

¿Cómo lo implementa GCC para ELF (Linux)?

Con la STB_LOCALencuadernación.

Si compilamos:

int i = 0;
static int si = 0;

y desmonte la tabla de símbolos con:

readelf -s main.o

la salida contiene:

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

entonces el enlace es la única diferencia significativa entre ellos. Valuees solo su desplazamiento en la .bsssección, por lo que esperamos que sea diferente.

STB_LOCALestá documentado en la especificación ELF en http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :

STB_LOCAL Los símbolos locales no son visibles fuera del archivo de objeto que contiene su definición. Los símbolos locales del mismo nombre pueden existir en varios archivos sin interferir entre sí

lo que lo convierte en una opción perfecta para representar static.

Las variables sin estática son STB_GLOBAL, y la especificación dice:

Cuando el editor de enlaces combina varios archivos de objetos reubicables, no permite múltiples definiciones de símbolos STB_GLOBAL con el mismo nombre.

que es coherente con los errores de enlace en múltiples definiciones no estáticas.

Si aumentamos la optimización con -O3, el sisímbolo se elimina por completo de la tabla de símbolos: de todos modos, no se puede usar desde afuera. TODO ¿por qué mantener las variables estáticas en la tabla de símbolos cuando no hay optimización? ¿Se pueden usar para algo? Tal vez para la depuración.

Ver también

Espacios de nombres anónimos C ++

En C ++, es posible que desee utilizar espacios de nombres anónimos en lugar de estáticos, lo que logra un efecto similar, pero oculta aún más las definiciones de tipo: espacios de nombres anónimos / sin nombre frente a funciones estáticas


39

Depende:

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

La función devolvería 1, 2, 3, etc. --- la variable no está en la pila.

C.A:

static int foo()
{
}

Significa que esta función solo tiene alcance en este archivo. Entonces ac y bc pueden tener diferentes foo()s, y foo no está expuesto a objetos compartidos. Entonces, si definiste foo en ac, no podrías acceder a él b.cdesde ningún otro lugar.

En la mayoría de las bibliotecas C, todas las funciones "privadas" son estáticas y la mayoría de las "públicas" no lo son.


18
+1 por mencionar x no en la pila o el montón. Está en el espacio de memoria estática.
Gob00st

1
@ Gob00st espacio de memoria estática? quisiste decir "Segmento de datos" ...?
Yousha Aleayoub

24

La gente sigue diciendo que 'estático' en C tiene dos significados. Ofrezco una forma alternativa de verlo que le da un significado único:

  • La aplicación de "estática" a un elemento obliga a ese elemento a tener dos propiedades: (a) No es visible fuera del alcance actual; (b) Es persistente.

La razón por la que parece tener dos significados es que, en C, cada elemento al que se puede aplicar 'estático' ya tiene una de estas dos propiedades , por lo que parece que ese uso particular solo involucra al otro.

Por ejemplo, considere las variables. Las variables declaradas fuera de las funciones ya tienen persistencia (en el segmento de datos), por lo que la aplicación 'estática' solo puede hacer que no sean visibles fuera del alcance actual (unidad de compilación). Por el contrario, las variables declaradas dentro de las funciones ya no tienen visibilidad fuera del alcance actual (función), por lo que la aplicación 'estática' solo puede hacerlas persistentes.

Aplicar 'estático' a las funciones es como aplicarlo a variables globales: el código es necesariamente persistente (al menos dentro del lenguaje), por lo que solo se puede alterar la visibilidad.

NOTA: Estos comentarios solo se aplican a C. En C ++, la aplicación 'estática' a los métodos de clase realmente le da a la palabra clave un significado diferente. De manera similar para la extensión de argumento de matriz C99.


Su (a) es redundante en el mejor de los casos. No hay ninguna variable visible fuera de su alcance. Esa es simplemente la definición de alcance. Lo que quiere decir se llama vinculación en el Estándar C. staticda enlace interno a un identificador.
Jens

16

De Wikipedia:

En el lenguaje de programación C, static se usa con variables y funciones globales para establecer su alcance en el archivo contenedor. En las variables locales, static se usa para almacenar la variable en la memoria asignada estáticamente en lugar de la memoria asignada automáticamente. Si bien el lenguaje no dicta la implementación de ninguno de los tipos de memoria, la memoria asignada estáticamente se reserva típicamente en el segmento de datos del programa en tiempo de compilación, mientras que la memoria asignada automáticamente se implementa normalmente como una pila de llamadas transitorias.


16

static significa cosas diferentes en diferentes contextos.

  1. Puede declarar una variable estática en una función C. Esta variable solo es visible en la función, sin embargo, se comporta como global, ya que solo se inicializa una vez y conserva su valor. En este ejemplo, cada vez que llame foo(), imprimirá un número creciente. La variable estática se inicializa solo una vez.

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
  2. Otro uso de static es cuando implementa una función o variable global en un archivo .c pero no desea que su símbolo sea visible fuera del .objgenerado por el archivo. p.ej

    static void foo() { ... }

8

Si declara una variable en una función estática, su valor no se almacenará en la pila de llamadas de función y seguirá estando disponible cuando vuelva a llamar a la función.

Si declara una variable global estática, su alcance estará restringido dentro del archivo en el que lo declaró. Esto es un poco más seguro que un global regular que se puede leer y modificar en todo el programa.


8

Odio responder una vieja pregunta, pero no creo que nadie haya mencionado cómo K&R lo explica en la sección A4.1 de "El lenguaje de programación C".

En resumen, la palabra estática se usa con dos significados:

  1. Estática es una de las dos clases de almacenamiento (la otra es automática). Un objeto estático mantiene su valor entre invocaciones. Los objetos declarados fuera de todos los bloques son siempre estáticos y no se pueden hacer automáticos.
  2. Pero, cuando la static palabra clave (gran énfasis en que se use en el código como palabra clave) se usa con una declaración, le da ese enlace interno al objeto, por lo que solo se puede usar dentro de esa unidad de traducción. Pero si la palabra clave se usa en una función, cambia la clase de almacenamiento del objeto (el objeto solo sería visible dentro de esa función de todos modos). Lo opuesto a estático es la externpalabra clave, que proporciona un enlace externo a un objeto.

Peter Van Der Linden da estos dos significados en "Programación experta C":

  • Dentro de una función, conserva su valor entre llamadas.
  • En el nivel de función, visible solo en este archivo.

Hay una tercera clase de almacenamiento, regístrese . Algunas personas también defienden una cuarta clase de almacenamiento, asignada , para el almacenamiento devuelto por malloc y sus amigos.
Jens

@Jens 'register' es solo una pista para el compilador; el almacenamiento de registros no se puede aplicar desde la fuente C. Entonces no lo consideraría una clase de almacenamiento.
GermanNerd

1
@GermanNerd Me temo que el Estándar ISO C no está de acuerdo con su punto de vista, ya que claramente hace registerun especificador de clase de almacenamiento (C99 6.7.1 Especificadores de clase de almacenamiento). Y es más que una simple sugerencia, por ejemplo, no puede aplicar la dirección del operador &en un objeto con clase de almacenamiento, registerindependientemente de si el compilador asigna un registro o no.
Jens

@Jens Gracias por recordarme sobre &. Podría haber hecho demasiado C ++ ..... De todos modos, aunque 'registrarse' es un especificador de clase de almacenamiento, en realidad el compilador probablemente creará el mismo código de máquina para el especificador 'automático' (inútil) que para el 'registro' 'especificador. Entonces, lo único que queda es la restricción a nivel de código fuente de no poder tomar una dirección. Por cierto, esta pequeña discusión me llevó a encontrar un error en Netbeans; ¡Desde mi última actualización, la cadena de herramientas g ++ está predeterminada en los nuevos proyectos de C!
GermanNerd

6

En C, estático tiene dos significados, dependiendo del alcance de su uso. En el ámbito global, cuando un objeto se declara a nivel de archivo, significa que ese objeto solo es visible dentro de ese archivo.

En cualquier otro ámbito, declara un objeto que retendrá su valor entre las diferentes veces que se ingresa el ámbito particular. Por ejemplo, si un int está delimitado dentro de un procedimiento:

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

   i++;
}

el valor de 'i' se inicializa a cero en la primera llamada al procedimiento, y el valor se retiene cada vez que se llama al procedimiento. si se imprimiera 'i', generaría una secuencia de 0, 1, 2, 3, ...


5

Es importante tener en cuenta que las variables estáticas en las funciones se inicializan en la primera entrada en esa función y persisten incluso después de que su llamada haya finalizado; en el caso de las funciones recursivas, la variable estática se inicializa solo una vez y persiste durante todas las llamadas recursivas e incluso después de que la llamada de la función haya finalizado.

Si la variable se ha creado fuera de una función, significa que el programador solo puede usar la variable en el archivo fuente en el que se ha declarado la variable.


5

Si declaras esto en un mytest.carchivo:

static int my_variable;

Entonces esta variable solo se puede ver desde este archivo. La variable no se puede exportar a ningún otro lado.

Si declara dentro de una función, el valor de la variable mantendrá su valor cada vez que se llame a la función.

Una función estática no se puede exportar desde fuera del archivo. Entonces, en un *.carchivo, está ocultando las funciones y las variables si las declara estáticas.


4

Las variables estáticas en C tienen la vida útil del programa.

Si se definen en una función, tienen alcance local, es decir, solo se puede acceder a ellas dentro de esas funciones. El valor de las variables estáticas se conserva entre las llamadas a funciones.

Por ejemplo:

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

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

En el programa anterior, varse almacena en el segmento de datos. Su vida útil es todo el programa C.

Después de la llamada a la función 1, se varconvierte en 2. Después de la llamada a la función 2, se varconvierte en 3.

El valor de varno se destruye entre llamadas a funciones.

Si se vartuviera entre una variable local y no estática, se almacenaría en el segmento de pila en el programa C. Dado que el marco de la pila de la función se destruye después de que la función regresa, el valor de vartambién se destruye.

Las variables estáticas inicializadas se almacenan en el segmento de datos del programa C, mientras que las no inicializadas se almacenan en el segmento BSS.

Otra información sobre estática: si una variable es global y estática, tiene el tiempo de vida del programa C, pero tiene alcance de archivo. Solo es visible en ese archivo.

Para probar esto:

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

Ahora intenta vincularlos usando:

gcc -o output file1.o file2.o

Daría un error de enlazador ya que x tiene el alcance de archivo de file1.c y el enlazador no podría resolver la referencia a la variable x utilizada en file2.c.

Referencias

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

Entiendo que los datos son persistentes, lo que significa que no se perderán después de cada llamada a la función, pero ¿por qué no static int var = 1;cambia el valor a uno cada vez
Eames,

3

Una variable estática es una variable especial que puede usar en una función y guarda los datos entre llamadas y no los elimina entre llamadas. Por ejemplo:

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

La salida:

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


Puede reemplazar printf("%d, ", count); count++;con `printf ("% d ", count ++) (no es que importe: P).
RastaJedi

2

Un valor variable estático persiste entre diferentes llamadas a funciones y su alcance está limitado al bloque local que una var estática siempre se inicializa con valor 0


2

Hay 2 casos:

(1) Variables locales declaradas static: asignadas en el segmento de datos en lugar de la pila. Su valor se retiene cuando vuelve a llamar a la función.

(2) Variables o funciones globales declaradas static: Invisible fuera de la unidad de compilación (es decir, son símbolos locales en la tabla de símbolos durante el enlace).


1

¡Las variables estáticas tienen la propiedad de preservar su valor incluso después de estar fuera de su alcance! Por lo tanto, las variables estáticas conservan su valor anterior en su alcance anterior y no se inicializan nuevamente en el nuevo alcance.

Mire esto, por ejemplo: una variable int estática permanece en la memoria mientras se ejecuta el programa. Una variable normal o automática se destruye cuando finaliza una llamada a la función donde se declaró la variable.

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

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

Esto generará: 1 2

Como 1 permanece en la memoria como se declaró estático

Las variables estáticas (como las variables globales) se inicializan como 0 si no se inicializan explícitamente. Por ejemplo, en el siguiente programa, el valor de x se imprime como 0, mientras que el valor de y es algo basura. Vea esto para más detalles.

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

Esto dará como resultado: 0 [some_garbage_value]

¡Estos son los principales que encontré que no se explicaron anteriormente para un novato!


-1

En la programación en C, statices una palabra clave reservada que controla tanto la vida útil como la visibilidad. Si declaramos una variable como estática dentro de una función, entonces solo será visible a través de esa función. En este uso, la vida útil de esta variable estática comenzará cuando se llame a una función y se destruirá después de la ejecución de esa función. puedes ver el siguiente ejemplo:

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

El programa anterior nos dará esta salida:

First Counter Output = 1 
Second Counter Output = 1 

Porque tan pronto como llamemos a la función, se inicializará count = 0. Y mientras ejecutamos counterFunction, destruirá la variable de conteo.


2
> El programa anterior nos dará esta Salida: Salida del primer contador = 1 Salida del segundo contador = 1 <No es cierto. Las variables estáticas se inicializan solo una vez. Entonces la salida será 1, luego 2, y así sucesivamente.
GermanNerd
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.