¿Qué es una función "estática" en C?


506

La pregunta era simple funciones, no static métodos, como se aclara en los comentarios.

Entiendo qué es una staticvariable, pero ¿qué es una staticfunción?

¿Y por qué si declaro una función, digamos void print_matrix, digamos a.c(SIN a.h) e incluyo "a.c"- obtengo "print_matrix@@....) already defined in a.obj", PERO si la declaro como static void print_matrixse compila?

ACTUALIZACIÓN Solo para aclarar las cosas: sé que incluir .ces malo, como muchos de ustedes señalaron. Solo lo hago para liberar espacio temporalmente main.chasta que tenga una mejor idea de cómo agrupar todas esas funciones en archivos .hy .carchivos apropiados . Solo una solución temporal y rápida.

Respuestas:


685

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

EDITAR : Para aquellos que pensaron que el autor de las preguntas significaba un 'método de clase': a medida que se etiqueta la pregunta, Cse refiere a una función de C simple y antigua. Para los métodos de clase (C ++ / Java / ...), staticsignifica que este método se puede invocar en la clase misma, no es necesaria ninguna instancia de esa clase.


2
En realidad no lo etiqueté como c ++, algunos administradores probablemente sí, pero se trataba de C ++, entonces, ¿cuál es la diferencia en C ++?
Slava V

16
Los métodos de C ++ a menudo se denominan "funciones miembro", por lo que estoy de acuerdo en que C ++ introduce un poco de ambigüedad. No es tu culpa: el idioma solo usa la palabra clave para dos cosas diferentes.
Chuck

2
No, todavía quiere decir una función de C ++. Una función libre de C ++ en lugar de una función miembro de C ++.
Carreras de ligereza en órbita

3
@Chuck: la terminología de C ++ nunca usa la palabra "método"; esa es la terminología de Java: en los documentos estándar de C ++ siempre se denomina "función miembro" (consulte esta respuesta o este glosario de términos C ++ vs Java (por ejemplo, C ++ usa "miembro de datos" y Java usa "campo", etc.)).
ShreevatsaR

66
Para aclarar un poco esta respuesta: el nombre de la función solo es visible para otras partes de la misma unidad de traducción, debajo de la primera declaración de ese nombre. La función se puede llamar desde otras unidades (y partes anteriores de la misma unidad) a través de otros medios, por ejemplo, un puntero de función.
MM

199

Hay una gran diferencia entre las funciones estáticas en C y las funciones miembro estáticas 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 se compila. En otras palabras, hacer una función estática limita su alcance. Puede pensar en una función estática como "privada" para su archivo * .c (aunque eso no es estrictamente correcto).

En C ++, "estático" también puede aplicarse a funciones miembro y miembros de datos de clases. Un miembro de datos estático también se denomina "variable de clase", mientras que un miembro de datos no estático 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ático. Por lo tanto, un miembro de datos estáticos es esencialmente una variable global, es decir, un miembro de una clase.

Las funciones de miembros no estáticos 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 en esto es que en C ++ los miembros de datos estáticos y las funciones de miembros estáticos no pertenecen a ningún objeto, sino a toda la clase.


42
C ++ también tiene archivos estáticos. No es necesario traer C a esto.
Carreras de ligereza en órbita

17
En C ++, una función estática es una función estática. Una función miembro estática es una función miembro estática, también conocida como método. El hecho de que C no tenga miembros no significa que las funciones sean "C".
Gerasimos R

3
¿Hay alguna diferencia entre var global y var estática de clase (excepto el espacio de nombres)?
Alexander Malakhov

3
El espacio de nombres es la principal diferencia. La otra diferencia es que puede hacer que un miembro de datos estáticos sea privado y, por lo tanto, solo sea accesible desde las funciones de miembro de la clase. En otras palabras, tiene mucho más control sobre un miembro de datos estáticos en comparación con una variable global.
Dima

2
¿Podría alguien explicar por qué pensar que una función estática como privada para su archivo .c no es estrictamente correcta? ¿Qué queda por decir?
YoTengoUnLCD

78

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 para que no pueda ser referenciada en otras unidades de traducción. Este uso está en desuso en C ++. Se prefieren espacios de nombres sin nombre 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 es en el contexto de una clase. Si una clase tiene una función miembro estática, eso significa que la función es miembro de la clase (y tiene el acceso habitual a otros miembros), pero no necesita ser invocada a través de un objeto en particular. En otras palabras, dentro de esa función, no hay un puntero "este".


1
La pregunta es sobre estática en c.
Deqing

8
@Deqing la pregunta fue originalmente etiquetada como C ++ y el autor habla sobre el uso de archivos ".cpp".
Brian Neal

57

Ejemplo mínimo de alcance de múltiples archivos ejecutable

Aquí ilustramos cómo staticafecta el alcance de las definiciones de funciones en múltiples archivos.

C.A

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * /programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
 */
/*void f() { puts("a f"); }*/

/* 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();
}

C Principal

#include <stdio.h>

void a(void);        

void f() { puts("main f"); }

static void sf() { puts("main sf"); }

void m() {
    f();
    sf();
}

int main() {
    m();
    a();
    return 0;
}

GitHub aguas arriba .

Compilar y ejecutar:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main

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 alcance, mejor, así que siempre declare funciones staticsi puede.

En la programación en C, los archivos se usan a menudo para representar "clases", y las staticfunciones representan métodos "privados" de la clase.

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

¿Qué dicen las normas al respecto?

C99 N1256 draft 6.7.1 "Especificadores de clase de almacenamiento" dice que statices un "especificador de clase de almacenamiento".

6.2.2 / 3 "Vínculos de identificadores" dice staticimplica internal linkage:

Si la declaración de un identificador de alcance 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 se internal linkagecomporta como en nuestro ejemplo:

En el conjunto de unidades de traducción y bibliotecas que constituyen un programa completo, cada declaración de un identificador particular con enlace externo denota el mismo objeto o función. Dentro de una unidad de traducción, cada declaración de un identificador con 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 la STB_LOCALencuadernación.

Si compilamos:

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

y desmonte la tabla de símbolos con:

readelf -s main.o

la salida 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. Valuees solo su desplazamiento en la .bsssección, por lo que esperamos que sea diferente.

STB_LOCALestá 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 de objeto que contiene su definición. Los símbolos locales del mismo nombre pueden existir en varios archivos sin interferir entre sí

lo que lo convierte en una opción perfecta para representar static.

Las funciones sin estática son STB_GLOBAL, y la especificación dice:

Cuando el editor de enlaces combina varios archivos de objetos 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 sfsímbolo se elimina por completo de la tabla de símbolos: de todos modos, no se puede usar desde afuera. TODO, ¿por qué mantener las funciones estáticas en la tabla de símbolos cuando no hay optimización? ¿Se pueden usar para algo?

Ver también

Espacios de nombres anónimos C ++

En C ++, es posible que desee utilizar espacios de nombres anónimos en lugar de estáticos, lo que logra un efecto similar, pero oculta aún más las definiciones de tipo: espacios de nombres anónimos / sin nombre frente a funciones estáticas


3
nota: void f() { puts("sf"); }(es decir, dos definiciones de f()) provoca un comportamiento indefinido sin necesidad de diagnóstico. Es un problema de calidad de enlace ver realmente un mensaje de error.
MM

2
¡Esta es la mejor y precisa explicación! ¡Que tú!
Aqua

20

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

Si solo tiene 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 compila de forma independiente y luego cada uno de esos archivos de objetos compilados (sample.o) se vinculan a un archivo ejecutable mediante el vinculador.

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 compilador crearía fácilmente archivos de objetos para esos dos módulos, pero el enlazador arrojará un error, porque encuentra dos funciones con el mismo nombre y no sabe cuál debería usar (incluso si no hay nada que 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 compilador no crea la típica bandera "puede vincular esta cosa" para el enlazador, de modo que el enlazador no vea esta función y no genere un error.


16

Primero: generalmente es una mala idea incluir un .cpparchivo en otro archivo, ya que genera problemas como este :-) La forma normal es crear unidades de compilación separadas y agregar un archivo de encabezado para el archivo incluido.

En segundo lugar:

C ++ tiene una terminología confusa aquí: no lo supe hasta que se señaló en los comentarios.

a) static functions- heredado de C, y de lo que estás hablando aquí. Fuera de cualquier clase. Una función estática significa que no es visible fuera de la unidad de compilació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- lo que Orientación de Objeto llama un método estático . Vive dentro de una clase. Llama a 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.


Bueno, lo hago solo para liberar un poco de espacio TEMPORALMENTE en main.cpp hasta que decida cómo organizar el archivo en bibliotecas junto con los .hpp adecuados. ¿Hay una mejor idea de cómo hacer esto?
Slava V

1
La terminología correcta en C ++ es la función miembro, no el método. No hay "métodos" en la jerga legal de C ++. Método es un término general de OO. C ++ los implementa a través de funciones miembro.
Brian Neal

14

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


7

Una función estática es aquella que se puede invocar en la clase misma, en oposición a 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 funcione sin tener una instancia. Esto a veces se usa en el patrón Factory:

Person* tom = Person::createNewPerson();

2
Me parece que estás hablando de "método" estático, no de "función".
Slava V

Supuse que te referías a funciones estáticas dentro de una clase.
Loros

Si he sabido que los "métodos" se llaman "funciones de método" en C ++, sería más claro al respecto. Bueno, ahora sí :) Gracias de todos modos
Slava V

55
No hay "métodos" en C ++, solo funciones. El estándar C ++ nunca menciona "métodos", solo "funciones".
Brian Neal

1
@ Charco Sé lo que estás diciendo, pero en el estándar C ++ no hay una definición de "método". C ++ solo tiene funciones, de varios tipos. "Método" es un término general de OO y se usa en otros lenguajes e informalmente en C ++. Un método se conoce formalmente como una "función miembro" en C ++.
Brian Neal

7

Nit menor: 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 se define la función. El error que está recibiendo se conoce comúnmente como violación de la Regla de una definición.

El estándar probablemente dice algo como:

"Cada programa contendrá exactamente una definición de cada función u objeto no en línea que se utiliza en ese programa; no se requiere diagnóstico".

Esa es la forma en C de ver las funciones estáticas. Sin embargo, esto está en desuso en C ++.

En C ++, además, puede declarar funciones miembro estáticas. Estas son principalmente metafunciones, es decir, no describen / modifican el comportamiento / estado de un objeto en particular, sino que actúan en 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 obtiene acceso a variables miembro estáticas desde dicha función.

Agregaría al ejemplo de Parrot el patrón 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 programa.


7

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

1) En lenguajes sin OOPS como C, significa que solo se puede acceder a la función dentro del archivo donde está definida.

2) En lenguajes con OOPS como C ++, significa que la función se puede invocar directamente en la clase sin crear una instancia de ella.


Esto no es verdad. La explicación de su segundo párrafo se refiere a " funciones miembro estáticas " de una clase, no a " funciones estáticas ". En C ++, una función calificada también statictiene un alcance de archivo, como en C.
RobertS es compatible con Monica Cellio el

0

Dado que la función estática solo es visible en este archivo. En realidad, el compilador puede hacer alguna optimización por usted si declara "estático" a alguna función.

Aquí hay un ejemplo simple.

C Principal

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

Y compilar con

gcc -o main main.c

Verás que falló. Porque incluso no implementas la función ghost ().

Pero, ¿y si usamos el siguiente comando?

gcc -DTEST -O2 -o main main.c

Es exitoso , y este programa puede ejecutarse normalmente.

¿Por qué? Hay 3 puntos clave.

  1. -O2: Nivel de optimización del compilador al menos 2.
  2. -DTEST: define TEST, por lo que no se llamará a test ().
  3. Definido "estático" para probar ().

Solo si estas 3 condiciones son todas verdaderas, puede pasar la compilación. Debido a esta declaración "estática", el compilador puede confirmar que nunca se llamará a test () en otro archivo. Su compilador puede eliminar test () al compilar. Como no necesitamos test (), no importa si ghost () está definido o implementado.


0

" ¿Qué es una staticfunción " "en C? "

Empecemos desde el principio.

Todo se basa en una cosa llamada "vinculación":

" Un identificador declarado en diferentes ámbitos o en el mismo ámbito más de una vez puede hacer referencia al mismo objeto o función mediante un proceso llamado enlace. 29) Hay tres tipos de enlace: externo, interno y ninguno " .

Fuente: C18, 6.2.2 / 1


"En el conjunto de unidades de traducción y bibliotecas que constituyen un programa completo, cada declaración de un identificador particular con enlace externo denota el mismo objeto o función. Dentro de una unidad de traducción, cada declaración de un identificador con enlace interno denota el mismo objeto o función . Cada declaración de un identificador sin vinculación denota una entidad única ".

Fuente: C18, 6.2.2 / 2


Si una función se define sin un especificador de clase de almacenamiento, la función tiene un externenlace por defecto:

"Si la declaración de un identificador para una función no tiene un especificador de clase de almacenamiento, su enlace se determina exactamente como si se declarara con el especificador de clase de almacenamiento externo ".

Fuente: C18, 6.2.2 / 5

Eso significa que, si su programa contiene varias unidades de traducción / archivos fuente ( .co .cpp), la función está visible en todas las unidades de traducción / archivos fuente que tiene su programa.

Esto puede ser un problema en algunos casos. ¿Qué sucede si desea utilizar dos funciones diferentes (definiciones), pero con el mismo nombre de función en dos contextos diferentes (en realidad, el contexto del archivo).

En C y C ++, el staticcalificador de clase de almacenamiento aplicado a una función en el ámbito del archivo (no una función miembro estática de una clase en C ++ o una función dentro de otro bloque) ahora ayuda y significa que la función respectiva solo es visible dentro de la unidad de traducción / archivo fuente en el que se definió y no en las otras TLU / archivos.

"Si la declaración de un identificador de alcance de archivo para un objeto o una función contiene el especificador de clase de almacenamiento estático , el identificador tiene un enlace interno. 30)"


30) Una declaración de función puede contener los especificadores de clase de almacenamiento estáticamente solo si está dentro del alcance del archivo; ver 6.7.1.

Fuente: C18, 6.2.2 / 3


Por lo tanto, una staticfunción solo tiene sentido si:

  1. Su programa contiene varias unidades de traducción / archivos fuente ( .co .cpp).

    y

  2. Desea limitar el alcance de una función al archivo, en el que se define la función específica.

Si no es así tanto de estos requisitos son correctos, no es necesario para envolver su cabeza en torno acerca de calificar como una función static.


Notas al margen:

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.