Прочитывая моего книжного Эксперта C Программирование, я столкнулся с главой по функциональному межрасположению и как это может привести к некоторым серьезным трудно находить ошибки, если сделано неумышленно.
Пример, данный в книге, следующий:
mktemp() { ... }
main() {
mktemp();
getwd();
}
mktemp(){ ... }
getwd(){ ...; mktemp(); ... }
Согласно книге, что происходит в main()
это mktemp()
(функция стандартной библиотеки для C), вставляется реализацией в my_source.c. Хотя имея main()
назовите мою реализацию mktemp()
предназначенное поведение, имея getwd()
(другая библиотечная функция C), также называют мою реализацию mktemp()
не.
По-видимому, этим примером была реальная ошибка, которая существовала в версии SunOS 4.0.3's lpr
. Книга продолжает объяснять, что фиксация должна была добавить ключевое слово static
к определению mktemp()
в my_source.c; хотя менять имя в целом должно было решить эту проблему также.
Эта глава оставляет меня с некоторыми неразрешенными вопросами, что я надеюсь Вы, парни могли ответить:
static
перед всеми функциями, что мы не хотим быть подвергнутыми?Спасибо за справку.
Я должен отметить, что мой вопрос только нацелен на вставку по функциям стандартной библиотеки для C, но также и функциям, содержавшимся в других библиотеках, возможно, третьей стороне, возможно, созданные внутренний. По существу я хочу поймать любой экземпляр межрасположения независимо от того, где вставленная функция находится.
Похоже, что вы хотите, чтобы инструменты обнаружили конфликты имен в функциях - т.е. вы не хотите, чтобы имена ваших внешне доступных функций случайно имели одинаковые имена и поэтому "перекрывали" или скрывали функции с одинаковыми именами в библиотеке.
Недавно на SO был вопрос, связанный с этой проблемой: Linking Libraries with Duplicate Class Names using GCC
Использование опции --whole-archive
для всех библиотек, с которыми вы связываете библиотеки, может помочь (но, как я упоминал в ответе там, я действительно не знаю, насколько хорошо это работает или насколько легко убедить сборки применять опцию ко всем библиотекам)
Чисто формально описываемое вами взаимное расположение является прямое нарушение правил определения языка C (правило ODR на языке C ++). Любой достойный компилятор должен либо обнаруживать эти ситуации, либо предоставлять возможности для их обнаружения. Просто незаконно определять более одной функции с одним и тем же именем на языке C, независимо от того, где эти функции определены (Стандартная библиотека, другая пользовательская библиотека и т. Д.)
Я понимаю, что многие платформы предоставляют средства для настройки [стандартного ] поведения библиотеки, определяя некоторые стандартные функции как слабые символы . Хотя это действительно полезная функция, я считаю, что компиляторы должны по-прежнему предоставлять пользователю средства для обеспечения стандартной диагностики (желательно для каждой функции или каждой библиотеки).
Итак, опять же, вам не следует беспокоиться о взаимном расположении, если в ваших библиотеках нет слабых символов. Если вы это сделаете (или если вы подозреваете, что это так), вы должны проконсультироваться с документацией вашего компилятора, чтобы узнать, предлагает ли он вам средства для проверки слабого разрешения символов.
В GCC, например, вы можете отключить функциональность слабых символов с помощью -fno-weak
, но это в основном убивает все, что связано со слабыми символами, что не всегда желательно.
Если к функции не нужно обращаться за пределами файла C, в котором она находится, то да, я бы рекомендовал сделать функцию статической
.
Одна вещь, которую вы можете сделать, чтобы избежать этого, - использовать редактор с настраиваемой подсветкой синтаксиса. Я лично использую SciTE, и я настроил его на отображение всех имен функций стандартной библиотеки красным цветом. Таким образом, легко заметить, если я повторно использую имя, которое не должен использовать (однако компилятор ничего не навязывает).
Относительно легко написать сценарий, который запускает nm -o
на всех ваших .o файлах и библиотеках и проверяет, определено ли внешнее имя как в вашей программе, так и в библиотеке. Это всего лишь одна из многих разумных услуг, которые компоновщик Unix не предоставляет, потому что он застрял в 1974 году, просматривая по одному файлу за раз. (Попробуйте расположить библиотеки в неправильном порядке и посмотрите, получите ли вы полезное сообщение об ошибке!)
Это действительно проблема компоновщика.
Когда вы компилируете кучу исходных файлов C, компилятор создает объектный файл для каждого из них. Каждый файл .o будет содержать список общедоступных функций в этом модуле, а также список функций, которые вызываются кодом в модуле, но фактически не определены там, то есть функции, которые этот модуль ожидает от какой-либо библиотеки.
Когда вы связываете группу файлов .o вместе, чтобы сделать исполняемый файл, компоновщик должен разрешить все эти отсутствующие ссылки. Это момент, когда может произойти вставка. Если есть неразрешенные ссылки на функцию с именем "mktemp" и несколько библиотек предоставляют общедоступную функцию с этим именем, какую версию следует использовать? На этот вопрос нет простого ответа, и да, могут произойти странные вещи, если будет выбран неправильный вариант
. Так что да, в C хорошей идеей будет «статизировать» все, если вам действительно не нужно использовать это из других исходных файлов. Фактически, во многих других языках это поведение по умолчанию, и вы должны пометить вещи как «общедоступные», если хотите, чтобы они были доступны извне.