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 */staticint 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){return42+ 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 */return0;}
¿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.
... 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.
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í:
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:000000000000067523 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 */staticvoid function1(void){
puts("function1 called");}And store the following code in another file ab1.c
/* file ab1.c */int main(void){
function1();
getchar();return0;}/* 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 */
Respuestas:
Hacer una función la
static
oculta de otras unidades de traducción, lo que ayuda a proporcionar encapsulación .helper_file.c
main.c :
fuente
#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.
fuente
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.
fuente
Mirando las publicaciones anteriores, me gustaría señalar un detalle.
Supongamos que nuestro archivo principal ("main.c") se ve así:
Ahora considere tres casos:
Caso 1: Nuestro archivo de encabezado ("header.h") tiene este aspecto:
Luego el siguiente comando en Linux:
tendrá éxito ! Después de eso si uno corre
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:
y también tenemos un archivo más "header.c", que se ve así:
Entonces el siguiente comando
dará un error
Caso 3:
Similar al caso 2, excepto que ahora nuestro archivo de encabezado ("header.h") es:
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.
fuente
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.
fuente
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
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.
fuente
Cuando sea necesario restringir el acceso a algunas funciones, usaremos la palabra clave estática mientras definimos y declaramos una función.
fuente