Estuvimos recabado en distintos espacios y de esta manera mostrarte la solución a tu dilema, si continúas con dudas déjanos la pregunta y responderemos porque estamos para servirte.
Solución:
De http://wiki.answers.com/Q/What_is_the_difference_between_static_and_extern:
los static La clase de almacenamiento se usa para declarar un identificador que es una variable local para una función o un archivo y que existe y retiene su valor después de que el control pasa desde donde fue declarado. Esta clase de almacenamiento tiene una duración permanente. Una variable declarada de esta clase conserva su valor de una llamada de la función a la siguiente. El alcance es local. Una variable es conocida solo por la función en la que se declara o si se declara globalmente en un archivo, es conocida o vista solo por las funciones dentro de ese archivo. Esta clase de almacenamiento garantiza que la declaración de la variable también inicializa la variable a cero o todos los bits desactivados.
los externo La clase de almacenamiento se usa para declarar una variable global que será conocida por las funciones en un archivo y capaz de ser conocida por todas las funciones en un programa. Esta clase de almacenamiento tiene una duración permanente. Cualquier variable de esta clase conserva su valor hasta que se cambia por otra asignación. El alcance es global. Una variable puede ser conocida o vista por todas las funciones dentro de un programa.
static
significa que una variable será conocida globalmente solo en este archivo. extern
significa que una variable global definida en otro archivo también será conocida en este archivo, y también se usa para acceder a funciones definidas en otros archivos.
Una variable local definida en una función también se puede declarar como static
. Esto provoca el mismo comportamiento que si estuviera definido como una variable global, pero solo es visible dentro de la función. Esto significa que obtiene una variable local cuyo almacenamiento es permanente y, por lo tanto, retiene su valor entre llamadas a esa función.
No soy un experto en C, por lo que podría estar equivocado sobre esto, pero así es como lo he entendido. static
y extern
. Es de esperar que alguien con más conocimientos pueda brindarle una mejor respuesta.
EDITAR: Respuesta corregida según comentario proporcionado por JeremyP.
Puede aplicar static
tanto a variables como a funciones. Hay dos respuestas que discuten el comportamiento de static
y extern
con respecto a las variables, pero ninguno cubre realmente las funciones. Este es un intento de rectificar esa deficiencia.
TL; DR
- Usar static funciones siempre que sea posible.
- Declare solo funciones externas en encabezados.
- Utilice los encabezados donde se definen las funciones y donde se utilizan las funciones.
- No declare funciones dentro de otras funciones.
- No explote la extensión GCC con definiciones de funciones anidadas dentro de otras funciones.
Funciones externas
De forma predeterminada, las funciones en C son visibles fuera de la unidad de traducción (TU, básicamente el archivo fuente de C y los encabezados incluidos) en la que están definidas. Estas funciones se pueden llamar por su nombre desde cualquier código que notifique al compilador que la función existe, generalmente mediante una declaración en un encabezado.
Por ejemplo, el encabezado
hace declaraciones visibles de funciones como printf()
, fprintf()
, scanf()
, fscanf()
, fopen()
, fclose()
, etcétera. Si un archivo de origen incluye el encabezado, puede llamar a las funciones. Cuando el programa está vinculado, se debe especificar la biblioteca correcta para satisfacer la definición de la función. Afortunadamente, el compilador de C proporciona automáticamente la biblioteca que proporciona (la mayoría de) las funciones en la biblioteca de C estándar (y generalmente proporciona muchas más funciones que solo esas). La advertencia de ‘la mayoría de’ se aplica porque en muchos sistemas (Linux, por ejemplo, pero no macOS), si usa funciones declaradas en el
encabezado, debe vincularlo con la biblioteca de matemáticas (biblioteca ‘matemáticas’ si es estadounidense), que generalmente se indica con la opción -lm
en la línea de comando del vinculador.
Tenga en cuenta que las funciones externas deben declararse en los encabezados. Cada función externa debe declararse en un encabezado, pero un encabezado puede declarar muchas funciones. El encabezado debe usarse tanto en la TU donde se define cada función como en cada TU que usa la función. Nunca debería necesitar escribir una declaración para una función global en un archivo de origen (a diferencia de un archivo de encabezado); debe haber un encabezado para declarar la función y debe usar ese encabezado para declararlo.
Funciones estáticas
Como alternativa a las funciones generalmente visibles, puede crear sus propias funciones static
. Esto significa que la función no se puede llamar por su nombre desde fuera de la TU en la que está definida. Es una función oculta.
La principal ventaja de static functions está ocultando detalles que el mundo exterior no necesita conocer. Es una técnica de ocultación de información básica pero poderosa. También sabe que si una función es static, no es necesario buscar usos de la función fuera de la TU actual, lo que puede simplificar enormemente la búsqueda. Sin embargo, si las funciones son static
, puede haber varias TU, cada una de las cuales contiene una definición de una función con el mismo nombre; cada TU tiene su propia función, que puede o no hacer lo mismo que una función con el mismo nombre en una TU diferente.
En mi código, califico todas las funciones excepto main()
con la palabra clave static
de forma predeterminada, a menos que haya un encabezado que declare la función. Si posteriormente necesito usar la función desde otro lugar, se puede agregar al encabezado apropiado y a la palabra clave static
eliminado de su definición.
Declarar funciones dentro de otras funciones
Es posible, pero muy desaconsejable, declarar una función dentro del alcance de otra función. Tales declaraciones van en contra de las máximas de desarrollo ágil como SPOT (punto único de verdad) y DRY (no se repita). También son una obligación de mantenimiento.
Sin embargo, puede, si lo desea, escribir código como:
extern int processor(int x);
int processor(int x)
extern int subprocess(int);
int sum = 0;
for (int i = 0; i < x; i++)
sum += subprocess((x + 3) % 7);
return sum;
extern int subprocess(int y);
int subprocess(int y)
return (y * 13) % 37;
La declaración en processor()
es suficiente para usar subprocess()
, pero por lo demás es insatisfactorio. los extern
La declaración antes de la definición es necesaria si usa opciones del compilador GCC como:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes
> -c process.c
process.c:12:5: error: no previous prototype for ‘subprocess’ [-Werror=missing-prototypes]
int subprocess(int y)
^~~~~~~~~~
cc1: all warnings being treated as errors
$
Creo que esta es una buena disciplina, similar a la que impone C ++. Es otra razón por la que hago la mayoría de las funciones. staticy defina las funciones antes de que se utilicen. La alternativa es declarar static funciones en la parte superior del archivo y luego definirlas en el orden que parezca apropiado. Ambas técnicas tienen algunos méritos; Prefiero evitar la necesidad de declarar y definir la misma función en el archivo definiendo antes de usar.
Tenga en cuenta que no puede declarar un static
función dentro de otra función, y si intenta definir una función como subprocess()
como un static función, el compilador da un error:
process.c:12:16: error: static declaration of ‘subprocess’ follows non-static declaration
static int subprocess(int y)
^~~~~~~~~~
process.c:5:20: note: previous declaration of ‘subprocess’ was here
extern int subprocess(int);
^~~~~~~~~~
Dado que las funciones que son visibles externamente deben declararse en un encabezado, no es necesario declararlas dentro de una función, por lo que nunca debe encontrarse con esto como un problema.
De nuevo, el extern
no es necesario en la declaración de la función dentro de la función; si se omite, se asume. Esto puede conducir a un comportamiento inesperado en programas para principiantes aquí en SO; a veces, encuentra una declaración de función donde se pretendía realizar una llamada.
Con GCC, la opción -Wnested-externs
identifica anidado extern
declaraciones.
Llamado por nombre vs llamado por puntero
Si tiene una disposición nerviosa, deje de leer ahora. ¡Esto se pone peludo!
El comentario 'llamado por nombre' significa que si tiene una declaración como:
extern int function(void);
puedes escribir tu código:
int i = function();
y el compilador y el enlazador resolverán las cosas para que se llame a la función y se utilice el resultado. los extern
en la declaración de la función es opcional pero explícito. Normalmente lo uso en un archivo de encabezado para que coincida con la declaración de esas variables globales raras, donde el extern
no es opcional sino obligatorio. Mucha gente no está de acuerdo conmigo en esto; haz lo que quieras (o debes).
Ahora que hay static funciones? Supongamos que la TU reveal.c
define una función static void hidden_function(int) …
. Luego, en otra TU openness.c
, no puedes escribir :
hidden_function(i);
Solo la TU que define la función oculta puede usarla directamente. Sin embargo, si hay una función en reveal.c
que devuelve un puntero de función al hidden_function()
, luego el código openness.c
puede llamar a esa otra función (por su nombre) para obtener un puntero a la función oculta.
reveal1.h
extern void (*(revealer(void)))(int);
Obviamente, esa es una función que no toma argumentos y devuelve un puntero a una función que toma un int
argumento y no devuelve ningún valor. No; no es bonito. Una de las veces que tiene sentido usar typedef
en punteros es con punteros a funciones (reveal2.h
):
typedef void (*HiddenFunctionType)(int);
extern HiddenFunctionType revealer(void);
Ahí: mucho más sencillo de entender.
Consulte ¿Es una buena idea escribir definiciones de punteros para una discusión general sobre el tema de typedef
y punteros; el breve resumen es "no es una buena idea excepto quizás con punteros de función".
reveal1.c
#include
#include "reveal1.h"
static void hidden_function(int x)
printf("%s:%s(): %dn", __FILE__, __func__, x);
extern void (*(revealer(void)))(int)
return hidden_function;
Sí, es legítimo (pero muy inusual) definir la función con un explícito extern
- Muy, muy pocas veces lo hago, pero aquí se enfatiza el papel de extern
y lo contrasta con static
. los hidden_function()
puede ser devuelto por revealer()
, y podría ser llamado por código dentro reveal.c
. Puede quitar el extern
sin cambiar el significado del programa.
openness1.c
#include
#include "reveal1.h"
int main(void)
void (*revelation)(int) = revealer();
printf("%s:%s: %dn", __FILE__, __func__, __LINE__);
(*revelation)(37);
return 0;
Este archivo no puede contener una llamada directa por su nombre a hidden_function()
porque está escondido en la otra TU. sin embargo, el revealer()
función declarada en reveal.h
se puede llamar por su nombre y devuelve un puntero a la función oculta, que luego se puede utilizar.
reveal2.c
#include
#include "reveal2.h"
static void hidden_function(int x)
printf("%s:%s(): %dn", __FILE__, __func__, x);
extern HiddenFunctionType revealer(void)
return hidden_function;
openness2.c
#include
#include "reveal2.h"
int main(void)
HiddenFunctionType revelation = revealer();
printf("%s:%s: %dn", __FILE__, __func__, __LINE__);
(*revelation)(37);
return 0;
Salidas de muestra
¡No es el resultado más emocionante del mundo!
$ openness1
openness1.c:main: 7
reveal1.c:hidden_function(): 37
$ openness2
openness2.c:main: 7
reveal2.c:hidden_function(): 37
$