Создание функции staticскрывает ее от других модулей перевода, что помогает обеспечить инкапсуляцию .
helper_file.c
int f1(int);/* prototype */staticint f2(int);/* prototype */int f1(int foo){return f2(foo);/* ok, f2 is in the same translation unit *//* (basically same .c file) as f1 */}int f2(int foo){return42+ foo;}
main.c :
int f1(int);/* prototype */int f2(int);/* prototype */int main(void){
f1(10);/* ok, f1 is visible to the linker */
f2(12);/* nope, f2 is not visible to the linker */return0;}
Является ли единица перевода правильной терминологией для использования здесь? Разве объектный файл не будет более точным? Из того, что я понимаю, статическая функция скрыта от компоновщика, и компоновщик не работает с модулями перевода.
Стивен Экхофф
2
Я должен был также сказать, что мне нравится думать, что это скрыто от компоновщика; это кажется более понятным.
Стивен Экхофф
1
Итак, внутреннюю функцию (чтобы мы не вызывали ее вне ее c-файла), мы должны поместить ее как статическую функцию, верно? Таким образом, мы можем быть уверены, что он не может позвонить в другом месте. Спасибо :)
14:30
1
Как вы это компилируете? Вы используете #include <helper_file.c>? Я думаю, это сделало бы это единым переводом ...
Atcold
2
@Atcold: как я написал код , который вы просто включите 2 исходные файлы в командной строке, как это gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c. Прототипы функций присутствуют в обоих исходных файлах (нет необходимости в заголовочных файлах). Компоновщик разрешит функции.
pmg
80
pmg - это место для инкапсуляции; помимо сокрытия функции от других модулей перевода (точнее, из- за этого), создание функций staticможет также принести выигрыш в производительности при наличии оптимизаций компилятора.
Поскольку staticфункция не может быть вызвана из-за пределов текущего блока преобразования (если код не берет указатель на свой адрес), компилятор контролирует все точки вызова в нем.
Это означает, что он может использовать нестандартный ABI, полностью его встроить или выполнять любое другое количество оптимизаций, которые могут быть невозможны для функции с внешним связыванием.
@caf Что вы подразумеваете под адресом функции? Для меня понятие функций / переменных, имеющих адреса или назначенный адрес во время компиляции, немного сбивает с толку. Можете ли вы уточнить?
SayeedHussain
2
@crypticcoder: ваша программа загружена в память, поэтому функции также имеют место в памяти и адрес может быть получен. С помощью указателя на функцию вы можете вызвать любой из них. Если вы это сделаете, это сократит список оптимизаций, которые может выполнить компилятор, поскольку код должен оставаться неизменным в одном и том же месте.
5
@crypticcoder: я имею в виду, что выражение оценивает указатель на функцию и делает с ней что-то еще, кроме немедленного вызова функции. Если указатель на staticфункцию выходит за пределы текущей единицы перевода, то эта функция может быть вызвана напрямую из других единиц перевода.
Кафе
@caf, если адрес функции взят, будет ли компилятор обнаруживать это и отключать статические функции оптимизации, упомянутые в этом ответе (например, с использованием нестандартного ABI)? Я предполагаю, что это должно было бы
севко
28
staticКлючевое слово в C используется в скомпилированный файл (.c в отличие от .h) , так что функция существует только в этом файле.
Обычно, когда вы создаете функцию, компилятор генерирует Cruft, который может использовать компоновщик, чтобы связать вызов функции с этой функцией. Если вы используете ключевое слово static, другие функции в том же файле могут вызывать эту функцию (потому что это может быть сделано без обращения к компоновщику), в то время как компоновщик не имеет информации, позволяющей другим файлам получать доступ к функции.
Программисты C используют статический атрибут, чтобы скрыть объявления переменных и функций внутри модулей, так же, как вы используете публичные и частные объявления в Java и C ++. C исходные файлы играют роль модулей. Любая глобальная переменная или функция, объявленная со статическим атрибутом, является частной для этого модуля. Точно так же любая глобальная переменная или функция, объявленная без статического атрибута, является общедоступной и может быть доступна любому другому модулю. Хорошей практикой программирования является защита ваших переменных и функций статическим атрибутом, где это возможно.
Ответ PMG очень убедителен. Если вы хотите знать, как работают статические объявления на уровне объектов, эта информация может быть вам интересна. Я повторно использовал ту же программу, написанную pmg, и скомпилировал ее в файл .so (общий объект)
Следующее содержание после сброса .so файла в нечто читаемое человеком
0000000000675 f1 : адрес функции f1
00000000006868c f2 : адрес функции f2 (staticc)
обратите внимание на разницу в адресе функции, это что-то значит. Для функции, которая объявлена с другим адресом, это может очень хорошо показать, что f2 живет очень далеко или в другом сегменте объектного файла.
Линкеры используют нечто, называемое PLT (таблица связывания процедур) и GOT (глобальная таблица смещений), чтобы понимать символы, к которым у них есть доступ к ссылкам.
А пока подумайте, что GOT и PLT магически связывают все адреса, а динамический раздел содержит информацию обо всех этих функциях, которые видны компоновщику.
После выгрузки динамического раздела .so файла мы получаем несколько записей, но заинтересованы только в функциях f1 и f2 .
Динамический раздел содержит запись только для функции f1 по адресу 0000000000000675, а не для f2 !
Num: Значение Размер Тип Bind Vis Ndx Имя
9:000000000000067523 FUNC GLOBAL DEFAULT 11 f1
И это все !. Из этого ясно, что компоновщик не сможет найти функцию f2, поскольку ее нет в динамическом разделе файла .so.
Когда необходимо ограничить доступ к некоторым функциям, мы будем использовать ключевое слово static при определении и объявлении функции.
/* file ab.c */staticvoid function1(void){
puts("function1 called");}And store the following code in another file ab1.c
/* file ab1.c */int main(void){
function1();
getchar();return0;}/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */
Ответы:
Создание функции
static
скрывает ее от других модулей перевода, что помогает обеспечить инкапсуляцию .helper_file.c
main.c :
источник
#include <helper_file.c>
? Я думаю, это сделало бы это единым переводом ...gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. Прототипы функций присутствуют в обоих исходных файлах (нет необходимости в заголовочных файлах). Компоновщик разрешит функции.pmg - это место для инкапсуляции; помимо сокрытия функции от других модулей перевода (точнее, из- за этого), создание функций
static
может также принести выигрыш в производительности при наличии оптимизаций компилятора.Поскольку
static
функция не может быть вызвана из-за пределов текущего блока преобразования (если код не берет указатель на свой адрес), компилятор контролирует все точки вызова в нем.Это означает, что он может использовать нестандартный ABI, полностью его встроить или выполнять любое другое количество оптимизаций, которые могут быть невозможны для функции с внешним связыванием.
источник
static
функцию выходит за пределы текущей единицы перевода, то эта функция может быть вызвана напрямую из других единиц перевода.static
Ключевое слово в C используется в скомпилированный файл (.c в отличие от .h) , так что функция существует только в этом файле.Обычно, когда вы создаете функцию, компилятор генерирует Cruft, который может использовать компоновщик, чтобы связать вызов функции с этой функцией. Если вы используете ключевое слово static, другие функции в том же файле могут вызывать эту функцию (потому что это может быть сделано без обращения к компоновщику), в то время как компоновщик не имеет информации, позволяющей другим файлам получать доступ к функции.
источник
Глядя на посты выше, я хотел бы указать одну деталь.
Предположим, что наш основной файл ("main.c") выглядит так:
Теперь рассмотрим три случая:
Случай 1: наш заголовочный файл ("header.h") выглядит так:
Затем следующая команда на Linux:
удастся ! После этого, если один бежит
Выход будет
Вызов функции внутри заголовка
Который должен печатать статическая функция.
Случай 2: наш заголовочный файл ("header.h") выглядит так:
и у нас также есть еще один файл "header.c", который выглядит следующим образом:
Тогда следующая команда
выдаст ошибку.
Случай 3:
Аналогичен случаю 2, за исключением того, что теперь наш заголовочный файл ("header.h"):
Тогда та же команда, что и в случае 2, будет успешной, и дальнейшее выполнение ./main даст ожидаемый результат.
Итак, из этих тестов (выполненных на машине Acer x86, ОС Ubuntu) я сделал предположение, что
Ключевое слово static не позволяет вызывать функцию в другом файле * .c, чем тот, в котором она определена.
Поправь меня, если я ошибаюсь.
источник
Программисты C используют статический атрибут, чтобы скрыть объявления переменных и функций внутри модулей, так же, как вы используете публичные и частные объявления в Java и C ++. C исходные файлы играют роль модулей. Любая глобальная переменная или функция, объявленная со статическим атрибутом, является частной для этого модуля. Точно так же любая глобальная переменная или функция, объявленная без статического атрибута, является общедоступной и может быть доступна любому другому модулю. Хорошей практикой программирования является защита ваших переменных и функций статическим атрибутом, где это возможно.
источник
Ответ PMG очень убедителен. Если вы хотите знать, как работают статические объявления на уровне объектов, эта информация может быть вам интересна. Я повторно использовал ту же программу, написанную pmg, и скомпилировал ее в файл .so (общий объект)
Следующее содержание после сброса .so файла в нечто читаемое человеком
0000000000675 f1 : адрес функции f1
00000000006868c f2 : адрес функции f2 (staticc)
обратите внимание на разницу в адресе функции, это что-то значит. Для функции, которая объявлена с другим адресом, это может очень хорошо показать, что f2 живет очень далеко или в другом сегменте объектного файла.
Линкеры используют нечто, называемое PLT (таблица связывания процедур) и GOT (глобальная таблица смещений), чтобы понимать символы, к которым у них есть доступ к ссылкам.
А пока подумайте, что GOT и PLT магически связывают все адреса, а динамический раздел содержит информацию обо всех этих функциях, которые видны компоновщику.
После выгрузки динамического раздела .so файла мы получаем несколько записей, но заинтересованы только в функциях f1 и f2 .
Динамический раздел содержит запись только для функции f1 по адресу 0000000000000675, а не для f2 !
Num: Значение Размер Тип Bind Vis Ndx Имя
И это все !. Из этого ясно, что компоновщик не сможет найти функцию f2, поскольку ее нет в динамическом разделе файла .so.
источник
Когда необходимо ограничить доступ к некоторым функциям, мы будем использовать ключевое слово static при определении и объявлении функции.
источник