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