resueltos - leer cadena de caracteres en c
Sin advertencia con cadena C sin inicializar (3)
En version.h
deberías declarar VERSION
como un extern
como
extern const char* const VERSION;
Y en version.c
debes definir la variable externa como
const char* const VERSION = "0.8 rev 213";
EDITAR: - También debe asegurarse de que solo un archivo de origen haya definido la variable VERSION
mientras que los otros archivos de origen lo declararon extern
y puede hacerlo definiéndolo en un archivo de origen VERSION.c
y VERSION.c
la declaración extern
en un archivo de encabezado. VERSION.h
.
Actualmente me pregunto por qué no recibo un error de GCC durante la compilación / vinculación de un programa de C pequeño.
version.h
en version.h
la siguiente cadena:
const char* const VERSION;
En version.c
he establecido la inicialización de la variable:
const char* const VERSION = "0.8 rev 213";
Ningún problema con eso. Puedo usar la cadena en el resto del programa.
Si falta el archivo c, no se produce ningún error durante la compilación / enlace, pero el programa falla con SIGSEGV (por supuesto) cuando intenta acceder a la variable.
¿Mi forma de configurar la variable VERSION
correcta o hay una mejor manera? ¿O existe la posibilidad de obtener un error durante la compilación / vinculación?
Has definido (no solo declarado) una variable en el encabezado.
Si alguna vez incluyó este encabezado de más de un archivo fuente, el comportamiento no está definido . Aquí está la cita relevante de la norma:
J.2 Comportamiento indefinido
...
Se usa un identificador con enlace externo, pero en el programa no existe exactamente una definición externa para el identificador, o el identificador no se usa y existen varias definiciones externas para el identificador.
...
Aquí confía en un comportamiento específico de GCC (en realidad, común a muchos compiladores, pero aún no estándar), que consiste en la fusión de definiciones de datos tentativos duplicados. Consulte la ayuda para las banderas de compilación GCC -fcommon
y -fno-common
. No todos los compiladores se comportan de esta manera. Históricamente, este es el comportamiento común de los enlazadores, porque así es como funcionó Fortran antes de que existiera C.
Asumiendo esta extensión de lenguaje, una de las definiciones (una que tiene un inicializador explícito) inicializa la variable para que apunte a su cadena literal. Pero si omite esta definición, seguirá siendo cero inicializado. Es decir, será un puntero nulo constante. No es muy útil.
Para hacer larga la historia corta, nunca hagas eso. Para declarar (pero no definir) una variable global en un encabezado, use extern
. Si lo hace, e intenta omitir una definición en otro lugar, es probable que obtenga un error de vinculador (aunque el estándar no requiere un diagnóstico para esta violación, todas las implementaciones conocidas producen una).
Su ejemplo funciona debido a una característica (mal) inspirada en Fortran de C (pero no C ++) llamada definiciones tentativas ( 6.9.2p2 ) que se extiende comúnmente pero no de manera estándar a varios archivos.
Las definiciones provisionales son declaraciones de variables sin extern
y sin inicializador. En las implementaciones comunes (juego de palabras destinado), las definiciones tentativas crean un tipo especial de símbolo que se denomina símbolo common
. Durante la vinculación, en presencia de un símbolo regular del mismo nombre, otros símbolos comunes se convierten en referencias al símbolo regular, lo que significa que todas las VERSION
vacías en sus unidades de traducción creadas allí debido a inclusiones se convertirán en referencias al símbolo regular const char* const VERSION = "0.8 rev 213";
. Si no existe tal símbolo regular, los símbolos comunes se fusionarán en una sola variable con cero inicializado.
No sé cómo obtener una advertencia contra esto con un compilador de C.
Esta
const char* const VERSION;
const char* const VERSION = "0.8 rev 213";
parece funcionar con gcc no importa lo que haya intentado ( g++
no lo aceptará; C ++ no tiene la función de definición provisional y no le gustan las variables const que no están inicializadas explícitamente). Pero puede compilar con -fno-common
(que es una opción no estándar bastante "común" (y muy recomendable) (gcc, clang y tcc lo tienen)) y luego obtendrá un error de vinculador si el no inicializado y Las declaraciones inicializadas sin menos están en diferentes unidades de traducción.
Ejemplo:
vc:
const char * VERSION;
C Principal
const char* VERSION;
int main(){}
compilación y vinculación:
gcc main.c v.c #no error because of tentative definitions
g++ main.c v.c #linker error because C++ doesn''t have tentative definitions
gcc main.c v.c -fno-common #linker error because tentative defs. are disabled
(Eliminé la segunda const
en el ejemplo por el ejemplo de C ++: C ++ además hace que las variables globales sean estáticas o algo similar, lo que complica la demostración de la característica de definición provisional).
Con las definiciones tentativas deshabilitadas o con C ++, todas las declaraciones de variables en los encabezados deben tener extern
version.h:
extern const char* const VERSION;
y debe tener exactamente una definición para cada global y esa definición debe tener un inicializador o estar sin extern
(algunos compiladores le avisarán si aplica extern
a una variable inicializada).
version.c:
#include "version.h" //optional; for type checking
const char* const VERSION = "0.8 rev 213";