¿Qué es una función “estática”?

La pregunta era acerca de las funciones c simples, no de static métodos static c ++ , como se aclara en los comentarios.

Ok, entiendo qué es una variable static , pero ¿qué es una función static ?

Y por qué es que si declaro una función, digamos void print_matrix , digamos ac (SIN ah ) e "print_matrix@@....) already defined in a.obj" "ac" – obtengo "print_matrix@@....) already defined in a.obj" , PERO si lo declaro como static void print_matrix , ¿comstack?

ACTUALIZAR Solo para aclarar las cosas, sé que incluir .c es malo, como muchos de ustedes señalaron. Solo lo hago para borrar temporalmente el espacio en main.c hasta que tenga una mejor idea de cómo agrupar todas esas funciones en los archivos .h y .c adecuados. Solo una solución temporal y rápida.

static funciones static son funciones que solo son visibles para otras funciones en el mismo archivo (más precisamente, la misma unidad de traducción ).

EDITAR : Para los que pensaban que el autor de las preguntas significaba un “método de clase”: como la pregunta está etiquetada como C se refiere a una antigua función C simple. Para los métodos de clase (C ++ / Java / …), static significa que se puede llamar a este método en la clase en sí, no es necesaria ninguna instancia de esa clase.

Hay una gran diferencia entre las funciones estáticas en C y las funciones de miembros estáticos en C ++. En C, una función estática no es visible fuera de su unidad de traducción, que es el archivo objeto en el que está comstackda. En otras palabras, hacer que una función sea estática limita su scope. Puede pensar en una función estática como “privada” en su archivo * .c (aunque eso no es estrictamente correcto).

En C ++, “estático” también se puede aplicar a las funciones miembro y a los miembros de datos de las clases. Un miembro de datos estáticos también se llama una “variable de clase”, mientras que un miembro de datos no estáticos es una “variable de instancia”. Esta es la terminología de Smalltalk. Esto significa que solo hay una copia de un miembro de datos estáticos compartida por todos los objetos de una clase, mientras que cada objeto tiene su propia copia de un miembro de datos no estáticos. Entonces, un miembro de datos estáticos es esencialmente una variable global, que es miembro de una clase.

Las funciones miembro no estáticas pueden acceder a todos los miembros de datos de la clase: estáticos y no estáticos. Las funciones de miembro estático solo pueden operar en los miembros de datos estáticos.

Una forma de pensar sobre esto es que en C ++ los miembros de datos estáticos y las funciones miembro estáticas no pertenecen a ningún objeto, sino a toda la clase.

Hay dos usos para la palabra clave static cuando se trata de funciones en C ++.

El primero es marcar la función como que tiene un enlace interno, por lo que no se puede referenciar en otras unidades de traducción. Este uso está en desuso en C ++. Espacios de nombre sin nombre son los preferidos para este uso.

 // inside some .cpp file: static void foo(); // old "C" way of having internal linkage // C++ way: namespace { void this_function_has_internal_linkage() { // ... } } 

El segundo uso está en el contexto de una clase. Si una clase tiene una función miembro estática, eso significa que la función es un miembro de la clase (y tiene el acceso habitual a otros miembros), pero no necesita invocarse a través de un objeto en particular. En otras palabras, dentro de esa función, no hay un puntero “this”.

Ejemplo mínimo de múltiples archivos ejecutables

ac :

 #include  /* Undefined behavior: already defined in main. Binutils 2.24 gives an error and refuses to link. https://stackoverflow.com/questions/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c */ /*void f() { puts("af"); }*/ /* OK: only declared, not defined. Will use the one in main. */ void f(void); /* OK: only visible to this file. */ static void sf() { puts("a sf"); } void a() { f(); sf(); } 

main.c :

 #include  void a(void); void f() { puts("main f"); } static void sf() { puts("main sf"); } void m() { f(); sf(); } int main() { m(); a(); return 0; } 

Comstackción :

 gcc -c ac -o ao gcc -c main.c -o main.o gcc -o main main.o ao 

Salida :

 main f main sf main f a sf 

Interpretación

  • hay dos funciones separadas sf , una para cada archivo
  • hay una sola función compartida f

Como de costumbre, cuanto menor sea el scope, mejor, por lo tanto, siempre declare las funciones static si puede.

En la progtwigción C, los archivos se usan a menudo para representar “clases”, y las funciones static representan métodos “privados” de la clase.

Un patrón C común es pasar una estructura como el primer argumento de “método”, que es básicamente lo que C ++ hace bajo el capó.

Qué dicen los estándares al respecto

C99 N1256 borrador 6.7.1 “Especificadores de clase de almacenamiento” dice que static es un “especificador de clase de almacenamiento”.

6.2.2 / 3 “Enlaces de identificadores” dice que static implica internal linkage :

Si la statement de un identificador de scope 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 internal linkage comporta como en nuestro ejemplo:

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

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

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

Con el enlace STB_LOCAL .

Si comstackmos:

 int f() { return 0; } static int sf() { return 0; } 

y desmontar la tabla de símbolos con:

 readelf -s main.o 

el resultado contiene:

 Num: Value Size Type Bind Vis Ndx Name 5: 000000000000000b 11 FUNC LOCAL DEFAULT 1 sf 9: 0000000000000000 11 FUNC GLOBAL DEFAULT 1 f 

entonces el enlace es la única diferencia significativa entre ellos. Value es solo su compensación en la sección .bss , por lo que esperamos que difiera.

STB_LOCAL está 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 objeto que contiene su definición. Los símbolos locales del mismo nombre pueden existir en múltiples archivos sin interferir entre ellos

que lo hace una elección perfecta para representar static .

Las funciones sin static son STB_GLOBAL , y la especificación dice:

Cuando el editor de enlaces combina varios archivos de objeto 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 símbolo sf se elimina por completo de la tabla de símbolos: no se puede utilizar desde el exterior de todos modos. TODO ¿por qué mantener las funciones estáticas en la tabla de símbolos cuando no hay optimización? Pueden ser utilizados para cualquier cosa?

Ver también

  • Lo mismo para las variables: https://stackoverflow.com/a/14339047/895245
  • extern es lo opuesto a static , y las funciones ya son extern por defecto: ¿Cómo uso extern para compartir variables entre los archivos fuente?

Inténtalo tú mismo

Ejemplo en GitHub para que juegues.

Lo siguiente es acerca de las funciones C simples: en una clase C ++, el modificador “estático” tiene otro significado.

Si tiene solo un archivo, este modificador no hace absolutamente ninguna diferencia. La diferencia viene en proyectos más grandes con múltiples archivos:

En C, cada “módulo” (una combinación de sample.c y sample.h) se comstack de forma independiente y luego cada uno de esos archivos de objetos comstackdos (sample.o) se vinculan entre sí a un archivo ejecutable por el enlazador.

Digamos que tiene varios archivos que incluye en su archivo principal y dos de ellos tienen una función que solo se usa internamente por conveniencia llamada add(int a, b) : el comstackdor creará fácilmente archivos de objeto para esos dos módulos, pero el El vinculador arrojará un error porque encuentra dos funciones con el mismo nombre y no sabe cuál debería usar (incluso si no hay nada para vincular, porque no se usan en otro lugar sino en su propio archivo).

Es por eso que hace que esta función, que solo se usa internamente, sea una función estática. En este caso, el comstackdor no crea el típico “usted puede vincular esto” -flag para el enlazador, de modo que el enlazador no vea esta función y no genere un error.

Primero: generalmente es una mala idea incluir un archivo .cpp en otro archivo; esto genera problemas como este 🙂 La forma normal es crear unidades de comstackción separadas y agregar un archivo de encabezado para el archivo incluido.

En segundo lugar:

C ++ tiene aquí una terminología confusa; no la conocía hasta que me señalaron en los comentarios.

a) static functions heredadas de C, y de lo que está hablando aquí. Fuera de cualquier clase. Una función estática significa que no es visible fuera de la unidad de comstackción actual, por lo que en su caso a.obj tiene una copia y su otro código tiene una copia independiente. (Hinchando el ejecutable final con múltiples copias del código).

b) static member function : qué Orientación de Objeto se refiere a un método estático. Vive dentro de una clase. Usted llama esto con la clase en lugar de a través de una instancia de objeto.

Estas dos definiciones diferentes de funciones estáticas son completamente diferentes. Ten cuidado, aquí hay dragones.

las definiciones de funciones estáticas marcarán este símbolo como interno. Por lo tanto, no será visible para vincular desde afuera, sino solo a funciones en la misma unidad de comstackción, generalmente el mismo archivo.

Una función estática es aquella que se puede invocar en la clase misma, a diferencia de una instancia de la clase.

Por ejemplo, un no estático sería:

 Person* tom = new Person(); tom->setName("Tom"); 

Este método funciona en una instancia de la clase, no en la clase misma. Sin embargo, puede tener un método estático que pueda funcionar sin tener una instancia. Esto a veces se usa en el patrón de fábrica:

 Person* tom = Person::createNewPerson(); 

Minit nit: las funciones estáticas son visibles para una unidad de traducción, que para la mayoría de los casos prácticos es el archivo en el que está definida la función. El error que está recibiendo se conoce comúnmente como violación de la regla de una sola definición.

El estándar probablemente dice algo como:

“Cada progtwig debe contener exactamente una definición de cada función u objeto no en línea que se utiliza en ese progtwig; no se requiere diagnóstico”.

Esa es la forma C de ver las funciones estáticas. Esto está en desuso en C ++ sin embargo.

En C ++, además, puede declarar que las funciones miembro están estáticas. Estas son en su mayoría metafunciones, es decir, no describen / modifican el comportamiento / estado de un objeto en particular, sino que actúan sobre toda la clase. Además, esto significa que no necesita crear un objeto para llamar a una función miembro estática. Además, esto también significa que solo tiene acceso a variables de miembros estáticos desde dicha función.

Agregaría al ejemplo de Parrot el patrón de Singleton que se basa en este tipo de función miembro estática para obtener / usar un solo objeto durante la vida útil de un progtwig.

La respuesta a la función estática depende del idioma:

1) En idiomas sin OOPS como C, significa que la función es accesible solo dentro del archivo donde está definida.

2) En idiomas con OOPS como C ++, significa que la función puede invocarse directamente en la clase sin crear una instancia de la misma.

para la función estática en “c” el comstackdor no creará sus variables internas en la stack, por lo que la llamada a la función estática es más rápida y, como consecuencia, no puede usar inicializadores como: char c = ‘A’.