статическая функция в C - PullRequest
167 голосов
/ 16 марта 2011

Какой смысл делать функцию статической в ​​C?

Ответы [ 6 ]

209 голосов
/ 16 марта 2011

Создание функции 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;
}
77 голосов
/ 16 марта 2011

pmg является точной инкапсуляцией; Помимо сокрытия функции от других модулей перевода (точнее, из-за ее), создание функций static может также обеспечить выигрыш в производительности при наличии оптимизации компилятора.

Поскольку функция static не может быть вызвана из-за пределов текущей единицы преобразования (если код не берет указатель на ее адрес), компилятор контролирует все точки вызова в ней.

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

27 голосов
/ 16 марта 2011

Ключевое слово static в C используется в скомпилированном файле (.c, а не .h), так что функция существует только в этом файле.

Обычно, когда вы создаете функцию, компилятор генерирует Cruft, который может использовать компоновщик, чтобы связать вызов функции с этой функцией. Если вы используете ключевое слово static, другие функции в том же файле могут вызывать эту функцию (потому что это может быть сделано без обращения к компоновщику), в то время как компоновщик не имеет информации, позволяющей другим файлам обращаться к функции.

8 голосов
/ 30 ноября 2015

Глядя на посты выше, я хотел бы указать одну деталь.

Предположим, наш основной файл ("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

предотвращаетбыть определенным в другом файле, отличном от того, где он объявлен.

Поправьте меня, если я ошибаюсь.

5 голосов
/ 16 декабря 2014

Программисты C используют статический атрибут, чтобы скрыть объявления переменных и функций внутри модулей, так же, как вы используете публичные и частные объявления в Java и C ++.C исходные файлы играют роль модулей.Любая глобальная переменная или функция, объявленная со статическим атрибутом, является закрытой для этого модуля.Точно так же любая глобальная переменная или функция, объявленная без статического атрибута, является общедоступной и может быть доступна любому другому модулю.Хорошей практикой программирования является защита ваших переменных и функций статическим атрибутом, где это возможно.

2 голосов
/ 30 ноября 2015

pmg ответ очень убедительный. Если вы хотите знать, как работают статические объявления на уровне объектов, эта информация может быть вам интересна. Я повторно использовал ту же программу, написанную pmg, и скомпилировал ее в файл .so (общий объект)

Следующее содержимое после сброса .so файла в нечто удобочитаемое для человека

0000000000000675 f1 : адрес функции f1

000000000000068c 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 файла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...