Какой смысл делать статическую функцию в C?
Какой смысл делать статическую функцию в C?
Ответы:
Создание функции static
скрывает ее от других модулей перевода, что помогает обеспечить инкапсуляцию .
helper_file.c
int f1(int); /* prototype */
static int 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) {
return 42 + 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 */
return 0;
}
#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") выглядит так:
#include "header.h"
int main(void) {
FunctionInHeader();
}
Теперь рассмотрим три случая:
Случай 1: наш заголовочный файл ("header.h") выглядит так:
#include <stdio.h>
static void FunctionInHeader();
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Затем следующая команда на Linux:
gcc main.c header.h -o main
удастся ! После этого, если один бежит
./main
Выход будет
Вызов функции внутри заголовка
Который должен печатать статическая функция.
Случай 2: наш заголовочный файл ("header.h") выглядит так:
static void FunctionInHeader();
и у нас также есть еще один файл "header.c", который выглядит следующим образом:
#include <stdio.h>
#include "header.h"
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Тогда следующая команда
gcc main.c header.h header.c -o main
выдаст ошибку.
Случай 3:
Аналогичен случаю 2, за исключением того, что теперь наш заголовочный файл ("header.h"):
void FunctionInHeader(); // keyword static removed
Тогда та же команда, что и в случае 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 Имя
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
И это все !. Из этого ясно, что компоновщик не сможет найти функцию f2, поскольку ее нет в динамическом разделе файла .so.
Когда необходимо ограничить доступ к некоторым функциям, мы будем использовать ключевое слово static при определении и объявлении функции.
/* file ab.c */
static void function1(void)
{
puts("function1 called");
}
And store the following code in another file ab1.c
/* file ab1.c */
int main(void)
{
function1();
getchar();
return 0;
}
/* 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 */