función estática en C

172

¿Cuál es el punto de hacer una función estática en C?

Cenoc
fuente
77
@nightcracker: No hay "métodos" en C ++. Creo que estás confundido con Objective-C.
Bo Persson
1
No, estoy confundido con Python. Una función dentro de una clase se llama método en Python.
orlp
66
posible duplicado de ¿Qué es una función "estática"? (en C)
atoMerz

Respuestas:

212

Hacer una función la staticoculta de otras unidades de traducción, lo que ayuda a proporcionar encapsulación .

helper_file.c

int f1(int);        /* prototype */
static int 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) {
    return 42 + 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 */
    return 0;
}
pmg
fuente
8
¿Es la unidad de traducción la terminología correcta para usar aquí? ¿No sería más exacto el archivo objeto? Por lo que entiendo, una función estática está oculta del enlazador y el enlazador no funciona en unidades de traducción.
Steven Eckhoff
2
También debería haber dicho que me gusta pensar que está oculto al enlazador; Parece más claro de esa manera.
Steven Eckhoff
1
Entonces, función interna (que seguramente no lo llamaremos fuera de su archivo c), deberíamos ponerlo como función estática, ¿verdad? Por lo tanto, podemos estar seguros de que no puede llamar a otra parte. Gracias :)
hqt
1
¿Cómo compilas esto? ¿Usas #include <helper_file.c>? Creo que eso lo convertiría en una sola unidad de traducción entonces ...
Atcold
2
@Atcold: la forma en que escribí el código simplemente incluye los 2 archivos de origen en la línea de comando, de esta manera gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c. Los prototipos para las funciones están presentes en ambos archivos de origen (sin necesidad de archivos de encabezado). El enlazador resolverá las funciones.
pmg
80

pmg es acertado sobre la encapsulación; más allá de ocultar la función de otras unidades de traducción (o más bien, debido a ello), hacer funciones statictambién puede conferir beneficios de rendimiento en presencia de optimizaciones del compilador.

Debido a staticque no se puede invocar una función desde cualquier lugar fuera de la unidad de traducción actual (a menos que el código lleve un puntero a su dirección), el compilador controla todos los puntos de llamada.

Esto significa que es gratis usar un ABI no estándar, alinearlo por completo o realizar cualquier cantidad de otras optimizaciones que podrían no ser posibles para una función con enlace externo.

Stephen Canon
fuente
9
... a menos que se tome la dirección de la función.
caf
1
@caf ¿Qué quieres decir con la dirección de la función? Para mí, la noción de funciones / variables que tienen direcciones o se les asigna una dirección en tiempo de compilación es un poco confusa. ¿Puedes por favor elaborar?
SayeedHussain
2
@crypticcoder: su programa se carga en la memoria, por lo tanto, las funciones también tienen una ubicación de memoria y se puede obtener la dirección. Con un puntero de función, puede llamar a cualquiera de esos. Si lo hace, reduce la lista de optimizaciones que puede realizar el compilador, ya que el código debe permanecer intacto en el mismo lugar.
55
@crypticcoder: quiero decir que una expresión evalúa un puntero a la función y hace algo con ella que no sea llamar inmediatamente a la función. Si un puntero a una staticfunción escapa a la unidad de traducción actual, entonces esa función podría llamarse directamente desde otras unidades de traducción.
caf
@caf si se toma la dirección de la función, ¿el compilador lo detectaría y desactivaría las optimizaciones de la función estática mencionadas en esta respuesta (por ejemplo, utilizando un ABI no estándar)? Supongo que tendría que hacerlo.
sevko
28

La staticpalabra clave en C se usa en un archivo compilado (.c en lugar de .h) para que la función solo exista en ese archivo.

Normalmente, cuando crea una función, el compilador genera una información que el enlazador puede usar para, bueno, vincular una llamada de función a esa función. Si usa la palabra clave estática, otras funciones dentro del mismo archivo pueden llamar a esta función (porque se puede hacer sin recurrir al enlazador), mientras que el enlazador no tiene información permitiendo que otros archivos accedan a la función.

3 doblones
fuente
1
3Doub: El uso de la palabra "cruft" es más preciso de lo que se le atribuye. En el contexto de la pregunta, "cruft" es la palabra correcta para usar aquí.
Erik Aronesty
@ 3Doubloons Estoy de acuerdo en que está simplificado, pero creo que eso lo hace mucho más fácil de entender para los principiantes.
Ingo Bürk
11

Mirando las publicaciones anteriores, me gustaría señalar un detalle.

Supongamos que nuestro archivo principal ("main.c") se ve así:

#include "header.h"

int main(void) {
    FunctionInHeader();
}

Ahora considere tres casos:

  • Caso 1: Nuestro archivo de encabezado ("header.h") tiene este aspecto:

    #include <stdio.h>
    
    static void FunctionInHeader();
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }

    Luego el siguiente comando en Linux:

    gcc main.c header.h -o main

    tendrá éxito ! Después de eso si uno corre

    ./main

    La salida será

    Función de llamada dentro del encabezado

    Que es lo que debería imprimir esa función estática.

  • Caso 2: Nuestro archivo de encabezado ("header.h") tiene este aspecto:

    static void FunctionInHeader();     

    y también tenemos un archivo más "header.c", que se ve así:

    #include <stdio.h>
    
    #include "header.h"
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }

    Entonces el siguiente comando

    gcc main.c header.h header.c -o main

    dará un error

  • Caso 3:

    Similar al caso 2, excepto que ahora nuestro archivo de encabezado ("header.h") es:

    void FunctionInHeader(); // keyword static removed

    Entonces, el mismo comando que en el caso 2 tendrá éxito, y la ejecución posterior ./main dará el resultado esperado.

Entonces, a partir de estas pruebas (ejecutadas en la máquina Acer x86, Ubuntu OS), asumí que

La palabra clave static impide que se llame a la función en otro archivo * .c que no sea donde está definido

Corrígeme si estoy equivocado.

mercurio0114
fuente
5

Los programadores de C usan el atributo estático para ocultar las declaraciones de variables y funciones dentro de los módulos, de la misma manera que usarías declaraciones públicas y privadas en Java y C ++. Los archivos fuente C juegan el papel de módulos. Cualquier variable o función global declarada con el atributo estático es privada para ese módulo. Del mismo modo, cualquier variable o función global declarada sin el atributo estático es pública y cualquier otro módulo puede acceder a ella. Es una buena práctica de programación proteger sus variables y funciones con el atributo estático siempre que sea posible.

Sistemas informáticos
fuente
4

La respuesta de pmg es muy convincente. Si desea saber cómo funcionan las declaraciones estáticas a nivel de objeto, esta información a continuación podría ser interesante para usted. Reutilicé el mismo programa escrito por pmg y lo compilé en un archivo .so (objeto compartido)

Los siguientes contenidos son después de volcar el archivo .so en algo legible por humanos

0000000000000675 f1 : dirección de la función f1

000000000000068c f2 : dirección de la función f2 (staticc)

Tenga en cuenta la diferencia en la dirección de la función, significa algo. Para una función que se declara con una dirección diferente, puede significar muy bien que f2 vive muy lejos o en un segmento diferente del archivo objeto.

Los enlazadores usan algo llamado PLT (tabla de vinculación de procedimientos) y GOT (tabla de compensaciones globales) para comprender los símbolos a los que tienen acceso para enlazar.

Por ahora, piense que GOT y PLT unen mágicamente todas las direcciones y una sección dinámica contiene información de todas estas funciones que son visibles por el enlazador.

Después de volcar la sección dinámica del archivo .so, obtenemos un montón de entradas, pero solo estamos interesados ​​en la función f1 y f2 .

¡La sección dinámica contiene la entrada solo para la función f1 en la dirección 0000000000000675 y no para f2 !

Num: Valor Tamaño Tipo Enlace Vis Ndx Nombre

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

Y eso es !. De esto queda claro que el enlazador no tendrá éxito en encontrar la función f2 ya que no está en la sección dinámica del archivo .so.

human.js
fuente
0

Cuando sea necesario restringir el acceso a algunas funciones, usaremos la palabra clave estática mientras definimos y declaramos una función.

            /* file ab.c */ 
static void function1(void) 
{ 
  puts("function1 called"); 
} 
And store the following code in another file ab1.c

/* file ab1.c  */ 
int main(void) 
{ 
 function1();  
  getchar(); 
  return 0;   
} 
/* 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 */
Amna Salman
fuente
Esta respuesta no es muy útil.
fiscblog