2013-03-15 2 views
6

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

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

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

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

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

ответ

6

Вы хотите расширение visibility attribute GCC.

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

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

(Вы, вероятно, хотите #ifdef вышеуказанные макросы, используя некоторые приемы конфигурации а-ля autoconf и другие Autotools, на других системах, вы бы просто иметь пустые определения, как #define PUBLIC_VISIBILITY /*empty*/ и т.д ...)

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

int module_var MODULE_VISIBILITY; 

или функция

void module_function (int) MODULE_VISIBILITY; 

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

См. Также вариант генерации кода GCC для -fvisibility.

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


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

+1

Что заставляет вас думать, что OP использует GCC и что переносимость кода нежелательна? – Lundin

+5

Я сказал, что это расширение GCC. Я добавил PS, повторяя это. –

+0

Да, да. Я прокомментировал, так как я не думаю, что Stack Overflow должен быть форумом GCC, все, кажется, неявно предполагают GCC в наши дни. – Lundin

4

Простейшее решение («старая школа») состоит в том, чтобы просто не объявлять переменную в предполагаемом публичном заголовке.

Разделите заголовок библиотек на «header.h» и «header-internal.h» и объявите внутренний материал в последнем.

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

Вы можете также обернуть переменную (переменные) в struct, чтобы сделать ее более чистым, так как только один фактический символ отображается на глобальном уровне.

+2

«Вы можете защитить код от Мерфи, но никогда не от Макиавелли». – aschepler

+0

Спасибо. Конечно, вы не должны помещать это в общий заголовочный файл, но, как я уже упоминал в моем вопросе, вы все равно можете получить доступ к переменной с соответствующим объявлением в своем приложении. Я стараюсь, чтобы имена переменных были ясными и краткими, поэтому вероятность того, что пользователь определит похожие имена, особенно. в контексте кода, использующего эту библиотеку, несколько выше нуля. Вот почему я ищу, чтобы защитить его. Однако включение их в «структуру» - это идея, которую я могу использовать для дальнейшего снижения этой вероятности. – ysap

+0

@ysap Если вы свяжете всю свою библиотеку с «чем-то», то есть не сырым исходным кодом, все такие проблемы с пространством имен исчезнут, и пользователю вашей библиотеки придется беспокоиться о публичном заголовке. – Lundin

2

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

struct data_s { 
    void *v; 
}; 

И где-то в источнике:

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

, а затем:

data.v = &gbss;

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

+0

Спасибо. В основном, что предложил @unwind. Обфускация - это прямое решение, но делает мой код более уродливым. – ysap

+0

@ysap Вы можете использовать макросы, чтобы сделать его немного более аккуратным. Это делает вещи более трудными, конечно, но гораздо сложнее найти глобальные переменные. – teppic

0

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

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

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

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

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

Смежные вопросы