Який сенс зробити функцію статичною в 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) , так що функція існує тільки в цьому файлі.
Зазвичай, коли ви створюєте функцію, компілятор генерує суворе, яке може використовувати лінкер, а також зв'язувати виклик функції з цією функцією. Якщо ви використовуєте статичне ключове слово, інші функції в тому ж файлі можуть викликати цю функцію (оскільки це можна зробити, не вдаючись до лінкера), в той час як у лінкера немає інформації, що дозволяє іншим файлам отримувати доступ до функції.
Розглядаючи повідомлення вище, я хотів би вказати одну деталь.
Припустимо, наш основний файл ("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 machine, Ubuntu OS) я зробив припущення, що
статичне ключове слово запобігає виклику функції в інший * .c файл, ніж там, де це визначено.
Виправте мене, якщо я помиляюся.
Програмісти C використовують статичний атрибут, щоб приховати декларації змінних та функцій всередині модулів, наскільки ви б використовували публічні та приватні декларації в Java та C ++. Файли з джерелом C відіграють роль модулів. Будь-яка глобальна змінна чи функція, оголошена статичним атрибутом, є приватною для цього модуля. Аналогічно, будь-яка глобальна змінна чи функція, оголошена без статичного атрибуту, є загальнодоступною та може отримати доступ до будь-якого іншого модуля. Це хороша практика програмування, щоб захистити свої змінні та функції статичним атрибутом, де це можливо.
Відповідь pmg дуже переконлива. Якщо ви хочете дізнатися, як працюють статичні декларації на рівні об'єктів, то ця інформація нижче може бути цікавою для вас. Я повторно використовував ту саму програму, яку написав pmg, і компілював її у файл .so (спільний об’єкт)
Наступний вміст знаходиться після скидання .so файлу в щось читабельне для людини
0000000000000675 f1 : адреса функції f1
000000000000068c f2 : адреса функції f2 (статична)
зауважте різницю в адресі функції, це щось означає. Для функції, яка оголошена з іншою адресою, вона може дуже добре означати, що f2 живе дуже далеко або в іншому сегменті файлу об'єкта.
Лінкери використовують те, що називається PLT (таблиця зв’язків процедур) та GOT (Глобальна таблиця зрушень), щоб зрозуміти символи, до яких вони мають доступ.
Поки думаєте, що GOT і PLT магічно прив'язують усі адреси, а динамічний розділ містить інформацію про всі ці функції, які видно лінкером.
Після демпінгу динамічного розділу файлу .so ми отримуємо купу записів, але їх цікавить лише функція f1 та f2 .
Динамічний розділ містить запис лише для функції f1 за адресою 0000000000000675, а не для f2 !
Число: Значення Розмір Тип Прив’язати Ім’я Vis Ndx
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
І це все !. З цього видно, що лінкер буде невдалий у пошуку функції f2, оскільки його немає в динамічному розділі файлу .so.
Коли виникає потреба обмежити доступ до деяких функцій, ми будемо використовувати статичне ключове слово, визначаючи та оголошуючи функцію.
/* 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 */