¿Cuál es el punto de hacer una función estática en C?
¿Cuál es el punto de hacer una función estática en C?
Respuestas:
Hacer una función la static
oculta 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;
}
#include <helper_file.c>
? Creo que eso lo convertiría en una sola unidad de traducción entonces ...
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 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 static
también puede conferir beneficios de rendimiento en presencia de optimizaciones del compilador.
Debido a static
que 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.
static
función escapa a la unidad de traducción actual, entonces esa función podría llamarse directamente desde otras unidades de traducción.
La static
palabra 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.
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.
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.
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.
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 */