c++ - incluir - headers en c
Declaraciones de variables en archivos de encabezado: estático o no? (12)
El libro C (gratuito en línea) tiene un capítulo sobre vinculación, que explica el significado de ''estático'' con más detalle (aunque la respuesta correcta ya se encuentra en otros comentarios): http://publications.gbdirect.co.uk/c_book/chapter4/linkage.html
Al refactorizar algunas #defines
encontré declaraciones similares a las siguientes en un archivo de encabezado de C ++:
static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;
La pregunta es, ¿qué diferencia, si hay alguna, creará la estática? Tenga en cuenta que la inclusión múltiple de los encabezados no es posible debido al clásico #ifndef HEADER
#define HEADER
#endif
truco (si eso es importante).
¿La estática significa que solo se crea una copia de VAL
, en caso de que el encabezado esté incluido por más de un archivo fuente?
La static
significa que habrá una copia de VAL
creada para cada archivo de origen en el que se incluye. Pero también significa que las inclusiones múltiples no darán lugar a múltiples definiciones de VAL
que colisionarán en el momento del enlace. En C, sin la static
necesitaría asegurarse de que solo un archivo fuente definiera VAL
mientras que los otros archivos fuente lo declararon extern
. Por lo general, uno haría esto definiéndolo (posiblemente con un inicializador) en un archivo fuente y extern
declaración extern
en un archivo de encabezado.
static
variables static
a nivel global solo son visibles en su propio archivo fuente, ya sea que hayan llegado allí a través de una inclusión o estén en el archivo principal.
Nota del editor: En C ++, los objetos const
sin palabras clave static
ni extern
en su declaración son implícitamente static
.
La declaración estática en este nivel de código significa que el variabel solo es visible en la unidad de compilación actual. Esto significa que solo el código dentro de ese módulo verá esa variable.
si tiene un archivo de encabezado que declara una variable estática y ese encabezado se incluye en múltiples archivos C / CPP, esa variable será "local" para esos módulos. Habrá N copias de esa variable para los N lugares donde se incluye el encabezado. No están relacionados entre sí en absoluto. Cualquier código dentro de cualquiera de esos archivos fuente solo hará referencia a la variable que se declara dentro de ese módulo.
En este caso particular, la palabra clave "estática" no parece proporcionar ningún beneficio. Puede que me esté perdiendo algo, pero parece que no importa, nunca antes había visto algo así.
En cuanto a la alineación, en este caso la variable probablemente esté en línea, pero eso es solo porque se ha declarado const. Es más probable que el compilador incorpore variables estáticas del módulo, pero eso depende de la situación y del código que se está compilando. No hay garantía de que el compilador inserte "estáticas".
La estática significará que obtendrá una copia por archivo, pero a diferencia de otros ha dicho que es perfectamente legal hacerlo. Puede probar esto fácilmente con una pequeña muestra de código:
test.h:
static int TEST = 0;
void test();
test1.cpp:
#include <iostream>
#include "test.h"
int main(void) {
std::cout << &TEST << std::endl;
test();
}
test2.cpp:
#include <iostream>
#include "test.h"
void test() {
std::cout << &TEST << std::endl;
}
Ejecutar esto le da esta salida:
0x446020
0x446040
Las etiquetas static
y extern
en las variables con ámbito de archivo determinan si están accesibles en otras unidades de traducción (es decir, otros archivos .c
o .cpp
).
static
proporciona la vinculación interna variable, ocultándola de otras unidades de traducción. Sin embargo, las variables con vinculación interna se pueden definir en múltiples unidades de traducción.extern
proporciona la vinculación externa variable, haciéndola visible para otras unidades de traducción. Típicamente esto significa que la variable solo debe definirse en una unidad de traducción.
El valor predeterminado (cuando no especifica static
o extern
) es una de esas áreas en las que C y C ++ son diferentes.
En C, las variables con ámbito de archivo son
extern
(vinculación externa) por defecto. Si usa C,VAL
esstatic
yANOTHER_VAL
esextern
.En C ++, las variables con ámbito de archivo son
static
(enlace interno) de forma predeterminada si sonconst
, yextern
de forma predeterminada si no lo son. Si usa C ++, tantoVAL
comoANOTHER_VAL
sonstatic
.
De un borrador de la especificación C :
6.2.2 Enlaces de identificadores ... -5- Si la declaración de un identificador para una función no tiene un especificador de clase de almacenamiento, su vinculación se determina exactamente como si se hubiera declarado con el especificador de clase de almacenamiento extern. Si la declaración de un identificador para un objeto tiene un alcance de archivo y no un especificador de clase de almacenamiento, su vinculación es externa.
A partir de un borrador de la especificación C ++ :
7.1.1 - Especificadores de clases de almacenamiento [dcl.stc] ... -6- Un nombre declarado en un ámbito de espacio de nombres sin un especificador de clase de almacenamiento tiene un enlace externo a menos que tenga un enlace interno debido a una declaración previa y siempre que no sea declarado const. Los objetos declarados const y no explícitamente declarados extern tienen un enlace interno.
No puede declarar una variable estática sin definirla también (esto es porque los modificadores de clase de almacenamiento estáticos y externos son mutuamente excluyentes). Una variable estática se puede definir en un archivo de cabecera, pero esto provocaría que cada archivo de origen que incluye el archivo de cabecera tenga su propia copia privada de la variable, que probablemente no sea la que se pretendía.
Para responder a la pregunta, "¿significa estático que solo se crea una copia de VAL, en caso de que el encabezado esté incluido por más de un archivo fuente?" ...
NO . VAL siempre se definirá por separado en cada archivo que incluya el encabezado.
Los estándares para C y C ++ causan una diferencia en este caso.
En C, las variables con ámbito de archivo son externas por defecto. Si usa C, VAL es estático y ANOTHER_VAL es externo.
Tenga en cuenta que los enlazadores modernos pueden presentar una queja acerca de ANOTHER_VAL si el encabezado está incluido en diferentes archivos (el mismo nombre global definido dos veces), y definitivamente se quejaría si ANOTHER_VAL se inicializara con un valor diferente en otro archivo.
En C ++, las variables con ámbito de archivo son estáticas por defecto si son const, y extern por defecto si no lo son. Si usa C ++, tanto VAL como ANOTHER_VAL son estáticos.
También debe tener en cuenta el hecho de que ambas variables se denominan const. Idealmente, el compilador siempre elegiría alinear estas variables y no incluir ningún almacenamiento para ellas. Hay una gran cantidad de razones por las que se puede asignar el almacenamiento. En los que puedo pensar ...
- opciones de depuración
- dirección tomada en el archivo
- el compilador siempre asigna almacenamiento (los tipos de const complejos no se pueden insertar fácilmente, por lo que se convierte en un caso especial para los tipos básicos)
Static evita que el compilador agregue instancias múltiples. Esto se vuelve menos importante con la protección #ifndef, pero suponiendo que el encabezado esté incluido en dos bibliotecas separadas, y la aplicación esté vinculada, se incluirán dos instancias.
Static evita que otra unidad de compilación extermine esa variable para que el compilador pueda simplemente "alinear" el valor de la variable donde se usa y no crear almacenamiento de memoria para ella.
En su segundo ejemplo, el compilador no puede suponer que algún otro archivo fuente no lo externará, por lo que debe almacenar ese valor en la memoria en algún lugar.
Suponiendo que estas declaraciones tienen un alcance global (es decir, no son variables de miembros), entonces:
estático significa ''enlace interno''. En este caso, dado que se declara const, el compilador puede optimizarlo / insertarlo. Si omite el const, entonces el compilador debe asignar almacenamiento en cada unidad de compilación.
Al omitir estática, el enlace es externo por defecto. De nuevo, la constidad le ha salvado: el compilador puede optimizar / usar en línea. Si suelta el const, entonces obtendrá un error de símbolos definidos de forma múltiple en el tiempo del enlace.
const
variables const
en C ++ tienen un enlace interno. Entonces, usar static
no tiene ningún efecto.
ah
const int i = 10;
one.cpp
#include "a.h"
func()
{
cout << i;
}
two.cpp
#include "a.h"
func1()
{
cout << i;
}
Si se tratara de un programa C, obtendría un error de ''definición múltiple'' para i
(debido a un enlace externo).
las variables const son por defecto estáticas en C ++, pero extern C. Así que si usas C ++ esto no tiene sentido qué construcción usar.
(7.11.6 C ++ 2003 y Apexndix C tiene muestras)
Ejemplo en comparar fuentes de compilación / enlace como C y C ++ programa:
bruziuz:~/test$ cat a.c
const int b = 22;
int main(){return 0;}
bruziuz:~/test$ cat b.c
const int b=2;
bruziuz:~/test$ gcc -x c -std=c89 a.c b.c
/tmp/ccSKKIRZ.o:(.rodata+0x0): multiple definition of `b''
/tmp/ccDSd0V3.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
bruziuz:~/test$ gcc -x c++ -std=c++03 a.c b.c
bruziuz:~/test$
bruziuz:~/test$ gcc --version | head -n1
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609