Ваш пример работает благодаря фортрановской (неправильной) возможности C (но не C ++), называемой предварительными определениями ( 6.9.2p2 ), которая обычно, но нестандартно, распространяется на несколько файлов.
Предварительные определения являются объявлениями переменных без extern
и с
нет инициализатора. В общих реализациях (предназначенных для каламбура) предварительные определения создают специальный вид символа, который называется символом common
. Во время связывания, при наличии регулярного символа с тем же именем, другие общие символы становятся ссылками на обычный символ, что означает, что все пустые VERSION
s в ваших единицах перевода, созданных там из-за включений, станут ссылками на обычный символ const char* const VERSION = "0.8 rev 213";
. Если такого регулярного символа нет, общие символы будут объединены в одну переменную с нулевым инициализацией.
Я не знаю, как получить предупреждение об этом с помощью компилятора C.
Это
const char* const VERSION;
const char* const VERSION = "0.8 rev 213";
, кажется, работает с gcc независимо от того, что я пробовал (g++
не примет это - C ++ не имеет функции предварительного определения и ему не нравятся переменные const, которые не инициализируются явно) , Но вы можете скомпилировать с -fno-common
(это довольно «распространенный» (и настоятельно рекомендуется) нестандартный параметр (у gcc, clang и tcc все есть)), и тогда вы получите ошибку компоновщика, если неинициализирован и инициализированные внешние объявления находятся в разных единицах перевода.
* * Пример тысяча двадцать-один: * * 1 022
v.c:
const char * VERSION;
main.c
const char* VERSION;
int main(){}
компиляция и компоновка:
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
(я удалил второй const
в примере ради примера C ++ - C ++ дополнительно делает статические глобальные константы статическими или что-то в этом роде, что только усложняет демонстрацию функции предварительного определения.)
Если предварительные определения отключены или в C ++ все объявления переменных в заголовках должны иметь extern
в них
version.h:
extern const char* const VERSION;
и у вас должно быть ровно одно определение для каждого глобального, и это определение должно иметь инициализатор или не иметь extern
(некоторые компиляторы будут предупреждать, если вы примените extern
к инициализированной переменной).
version.c:
#include "version.h" //optional; for type checking
const char* const VERSION = "0.8 rev 213";