Как скрыть глобальную переменную, которая отображается в нескольких файлах?

Я пишу библиотеку C (shared). Он начинался как единая единица перевода, в которой я мог определить пару static глобальные переменные, которые будут скрыты от внешних модулей.

Теперь, когда библиотека выросла, я хочу разбить модуль на несколько меньших исходных файлов. Проблема в том, что теперь у меня есть два варианта для указанных глобалс:

  1. иметь частные копии в каждом исходном файле и каким-то образом синхронизировать их значения с помощью вызовов функций - это будет очень некрасиво очень быстро.

  2. удалить static определение, поэтому переменные совместно используются во всех единицах перевода с помощью extern - но теперь код приложения, связанный с библиотекой, может получить доступ к этим глобалам, если там сделано требуемое объявление.

Итак, есть ли аккуратный способ сделать частную глобальную переменную общей для нескольких конкретных единиц перевода?

4 ответов


вы хотите атрибут visibility расширение GCC.

практически, что-то вроде:

 #define MODULE_VISIBILITY  __attribute__ ((visibility ("hidden")))
 #define PUBLIC_VISIBILITY  __attribute__ ((visibility ("default")))

(вы, вероятно, хотите, чтобы #ifdef вышеуказанные макросы, используя некоторые трюки конфигурации à la autoconfи другие autotools; в других системах у вас просто будут пустые определения, такие как #define PUBLIC_VISIBILITY /*empty*/ etc...)

затем объявить переменную:

int module_var  MODULE_VISIBILITY;

или функция

void module_function (int) MODULE_VISIBILITY;

затем вы можете использовать module_var или позвоните module_function внутри вашей общей библиотеки, но не снаружи.

см. также -fvisibility вариант поколения кода GCC.

кстати, вы также можете скомпилировать всю свою библиотеку с помощью -Dsomeglobal=alongname3419a6 и использовать someglobal как обычно; чтобы действительно найти его, вашему пользователю нужно будет передать то же определение препроцессора компилятору, и вы можете сделать имя alongname3419a6 случайный и маловероятный достаточно, чтобы сделать столкновение маловероятно.


PS. Эта видимость специфический для GCC (и, вероятно,для общих библиотек ELF например, в Linux). Он не будет работать без GCC или вне общих библиотек.... так что вполне специфичен Linux (даже если некоторые другие системы, возможно, Solaris с GCC, имеют его). Возможно, некоторые другие компиляторы (clang С LLVM) может также поддержать это на Linux на общий библиотеки (не статические). На самом деле, реальное скрытие (для нескольких единиц компиляции одной общей библиотеки) выполняется в основном компоновщиком (потому что эльф общие библиотеки позволяют это).


самое простое ("олдскульное") решение-просто не объявлять переменную в предполагаемом открытом заголовке.

разделите заголовок библиотеки на " header.H" и заголовок-внутреннее".h", и объявить внутренний материал в последнем.

конечно, вы также должны позаботиться о защите имени вашей библиотеки-глобальной переменной, чтобы она не сталкивалась с кодом пользователя; предположительно, у вас уже есть префикс, который вы используете для функций для этой цели.

вы можно также обернуть переменную (ы) в struct, чтобы сделать его чище с тех пор только один символ виден в глобальном масштабе.


вы можете запутать вещи с замаскированными структурами, если вы действительно хотите скрыть информацию как можно лучше. например, в заголовочном файле

struct data_s {
   void *v;
};

и где-то в вашем источнике:

struct data_s data;
struct gbs {
   // declare all your globals here
} gbss;

и затем:

data.v = &gbss;

вы можете получить доступ ко всем глобалам через:((struct gbs *)data.v)->


Я знаю, что это не будет то, что вы буквально намеревались, но вы можете оставить глобальные переменные статическими и разделить их на несколько исходных файлов.

скопируйте функции, которые записывают в соответствующую статическую переменную в том же исходном файле, также объявленном статическим.

объявите функции, которые читают статическую переменную, чтобы внешние исходные файлы того же модуля могли прочитать ее значение.

таким образом делая его менее глобальные. Если возможно, лучшая логика для разбиения больших файлов на более мелкие, это принять решение на основе данных.

Если это невозможно сделать таким образом, чем вы можете поднять все глобальные переменные в один исходный файл как статические и получить к ним доступ из других исходных файлов модуля по функциям, что делает его официальным, так что если кто-то манипулирует вашими глобальными переменными, по крайней мере, вы знаете, как. Но тогда, вероятно, лучше использовать метод @unwind.