Локальная статическая переменная в шаблоне в общей библиотеке [дубликат]

Обычно используется xargs , чтобы сделать вывод одной команды опцией другой команды. Например:

$ cat command1
#!/bin/sh

echo "one"
echo "two"
echo "three"

$ cat command2
#!/bin/sh

printf '1 = %s\n' "$1"

$ ./command1 | xargs -n 1 ./command2
1 = one
1 = two
1 = three
$ 

Но ... пока это был ваш вопрос, это не то, что вы действительно хотите знать.

Если вы не возражаете хранить свой tty в переменную, вы можете использовать переменную bash для замены:

$ tty=`tty`; who | grep -w "${tty#/dev/}"
ghoti            pts/198  Mar  8 17:01 (:0.0)

(вы хотите использовать -w, потому что если вы находитесь на pts / 6, вы не должны видеть лоты pts / 60.)

Вы ограничены этим в переменной, потому что если вы попытаетесь поместить команду tty в канал, она думает, что она больше не работает с терминалом.

$ true | echo `tty | sed 's:/dev/::'`
not a tty
$ 

Обратите внимание, что ничто в этом ответе пока не относится к bash. Поскольку вы используете bash, другим способом решения этой проблемы является замена процесса. Например, пока это не работает:

$ who | grep "$(tty | sed 's:/dev/::')"

Это делает:

$ grep $(tty | sed 's:/dev/::') < <(who)

85
задан Jose L 30 August 2016 в 17:24
поделиться

1 ответ

Это довольно известное различие между Windows и Unix-подобными системами.

Независимо от того, что:

  • Каждый процесс имеет свои собственные адресное пространство, означающее, что между процессами никогда не используется какая-либо память (если вы не используете некоторую межпроцессную библиотеку или расширения связи).
  • Правило One Definition (ODR) все еще сохраняется применяется, что означает, что вы можете иметь только одно определение глобальной переменной, видимое во время ссылки (статическое или динамическое связывание).

Итак, ключевая проблема здесь - действительно видимость .

Во всех случаях глобальные переменные (или функции) static никогда не видны извне модуля (dll / so или executable). Стандарт C ++ требует, чтобы у них была внутренняя связь, что означает, что они не видны за пределами единицы перевода (которая становится объектным файлом), в которой они определены. Итак, это решает эту проблему.

Там, где это усложняется, вы имеете extern глобальные переменные. Здесь ОС и Unix-подобные системы совершенно разные.

В случае Windows (.exe и .dll) глобальные переменные extern не являются частью экспортированных символов. Другими словами, разные модули никоим образом не знают глобальных переменных, определенных в других модулях. Это означает, что вы получите ошибки компоновщика, если попытаетесь, например, создать исполняемый файл, который должен использовать переменную extern, определенную в DLL, потому что это недопустимо. Вам нужно будет предоставить объектный файл (или статическую библиотеку) с определением этой внешней переменной и связать его статически с как с исполняемым файлом, так и с DLL, в результате получится две различные глобальные переменные (одна из которых принадлежит исполняемый и один, принадлежащий к DLL).

Чтобы фактически экспортировать глобальную переменную в Windows, вы должны использовать синтаксис, аналогичный синтаксису экспорта / импорта функции, то есть:

#ifdef COMPILING_THE_DLL
#define MY_DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define MY_DLL_EXPORT extern "C" __declspec(dllimport)
#endif

MY_DLL_EXPORT int my_global;

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

В случае Unix-подобных сред (например, Linux) динамическая библиотеки, называемые «общие объекты» с расширением .so экспортируют все extern глобальные переменные (или функции). В этом случае, если вы выполняете время загрузки , связанное из любого места в общий файл объекта, тогда глобальные переменные являются совместно используемыми, т. Е. Связаны друг с другом как единое целое. В принципе, Unix-подобные системы разработаны таким образом, что практически нет никакой разницы между связыванием со статической или динамической библиотекой. Опять же, ODR применяется по всем направлениям: глобальная переменная extern будет разделяться между модулями, а это означает, что у нее должно быть только одно определение для всех загруженных модулей.

Наконец, в обоих случаях для Windows или Unix-подобных системах вы можете выполнить время выполнения динамической библиотеки, то есть используя либо LoadLibrary() / GetProcAddress() / FreeLibrary(), либо dlopen() / dlsym() / dlclose(). В этом случае вам нужно вручную получить указатель на каждый из символов, которые вы хотите использовать, и который включает в себя глобальные переменные, которые вы хотите использовать. Для глобальных переменных вы можете использовать GetProcAddress() или dlsym() так же, как и для функций, при условии, что глобальные переменные являются частью списка экспортированных символов (по правилам предыдущих абзацев).

И, конечно же, в качестве необходимой окончательной заметки: следует избегать глобальных переменных. И я считаю, что текст, который вы цитировали (о том, что «неясно»), относится точно к различиям, специфичным для платформы, которые я только что объяснил (динамические библиотеки на самом деле не определены стандартом C ++, это платформа, гораздо менее надежна / переносима).

129
ответ дан Mikael Persson 16 August 2018 в 08:02
поделиться
  • 1
    Отличный ответ, спасибо! У меня есть продолжение: поскольку DLL является самодостаточной частью кода и amp; данных, имеет ли раздел сегмента данных аналогичный исполняемым файлам? Я пытаюсь понять, где и как эти данные загружаются (в) при использовании общей библиотеки. – Raja 15 October 2013 в 19:24
  • 2
    @Raja Да, DLL имеет сегмент данных. Фактически, с точки зрения самих файлов, исполняемые файлы и библиотеки DLL практически идентичны, единственная реальная разница - это флаг, который установлен в исполняемом файле, чтобы сказать, что он содержит «главную», функция. Когда процесс загружает DLL, его сегмент данных копируется где-то в адресное пространство процесса, а статический код инициализации (который инициализирует нетривиальные глобальные переменные) также запускается в адресном пространстве процесса. Загрузка такая же, как и для исполняемого файла, за исключением того, что пространство адресов процесса расширяется, а не создается новое. – Mikael Persson 15 October 2013 в 23:40
  • 3
    Спасибо, что разъяснил это. – Raja 15 October 2013 в 23:56
  • 4
    «В принципе, Unix-подобные системы разработаны таким образом, что практически нет никакой разницы между связыванием со статической или динамической библиотекой». - это философский нюанс, который не известен большинству разработчиков Windows! Благодарю. Могу ли я привести вас в сообщение о кросс-платформенной разработке Win / iOS / Android? – Brian Haak 10 July 2014 в 15:41
  • 5
    Что делать, если MY_DLL_EXPORT ResourceObject my_global;, где ResourceObject имеет нетривиальный dtor. Будет ли dtor вызываться, когда программа, связывающая DLL, завершается? – AntiMoron 15 October 2015 в 08:12
  • 6
Другие вопросы по тегам:

Похожие вопросы: