Jaki jest sens statycznej funkcji w C?
Jaki jest sens statycznej funkcji w C?
Odpowiedzi:
Utworzenie funkcji static
ukrywa ją przed innymi jednostkami tłumaczeniowymi, co pomaga zapewnić hermetyzację .
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>
? Myślę, że wtedy byłaby to pojedyncza jednostka tłumaczeniowa ...
gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. Prototypy funkcji znajdują się w obu plikach źródłowych (nie ma potrzeby plików nagłówkowych). Konsolidator rozwiąże funkcje.
pmg mówi o hermetyzacji; poza ukryciem funkcji przed innymi jednostkami tłumaczeniowymi (a raczej z tego powodu ), tworzenie funkcji static
może również przynosić korzyści w zakresie wydajności w obecności optymalizacji kompilatora.
Ponieważ static
funkcji nie można wywołać z dowolnego miejsca spoza bieżącej jednostki tłumaczenia (chyba że kod pobiera wskaźnik do jej adresu), kompilator kontroluje wszystkie punkty wywołania do niej.
Oznacza to, że można swobodnie używać niestandardowego interfejsu ABI, całkowicie go wbudować lub wykonać dowolną liczbę innych optymalizacji, które mogą nie być możliwe w przypadku funkcji z połączeniem zewnętrznym.
static
funkcji wymyka się z bieżącej jednostki tłumaczeniowej, wówczas funkcja ta może być wywołana bezpośrednio z innych jednostek tłumaczeniowych.
Słowo static
kluczowe w C jest używane w skompilowanym pliku (.c w przeciwieństwie do .h), więc funkcja istnieje tylko w tym pliku.
Normalnie, kiedy tworzysz funkcję, kompilator generuje cruft, którego konsolidator może użyć do, no cóż, połączenia wywołania funkcji z tą funkcją. Jeśli użyjesz słowa kluczowego static, inne funkcje w tym samym pliku mogą wywołać tę funkcję (ponieważ można to zrobić bez uciekania się do konsolidatora), podczas gdy konsolidator nie ma informacji umożliwiających innym plikom dostęp do funkcji.
Patrząc na powyższe posty, chciałbym zwrócić uwagę na jeden szczegół.
Załóżmy, że nasz główny plik („main.c”) wygląda następująco:
#include "header.h"
int main(void) {
FunctionInHeader();
}
Rozważmy teraz trzy przypadki:
Przypadek 1: Nasz plik nagłówkowy („header.h”) wygląda następująco:
#include <stdio.h>
static void FunctionInHeader();
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Następnie następujące polecenie w systemie Linux:
gcc main.c header.h -o main
odniesie sukces ! Po tym, jeśli ktoś biegnie
./main
Dane wyjściowe będą
Wywołanie funkcji w nagłówku
Co powinna wydrukować ta statyczna funkcja.
Przypadek 2: Nasz plik nagłówkowy („header.h”) wygląda następująco:
static void FunctionInHeader();
i mamy jeszcze jeden plik „header.c”, który wygląda tak:
#include <stdio.h>
#include "header.h"
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Następnie następujące polecenie
gcc main.c header.h header.c -o main
wyświetli błąd.
Przypadek 3:
Podobnie jak w przypadku 2, z tą różnicą, że teraz nasz plik nagłówkowy („header.h”) to:
void FunctionInHeader(); // keyword static removed
Wtedy to samo polecenie co w przypadku 2 zakończy się sukcesem, a dalsze wykonanie ./main da oczekiwany rezultat.
Więc z tych testów (wykonanych na komputerze Acer x86, Ubuntu OS) założyłem, że
Słowo kluczowe static zapobiega wywołaniu funkcji w innym pliku * .c niż jest to zdefiniowane.
Popraw mnie, jeśli się mylę.
Programiści C używają statycznego atrybutu do ukrywania deklaracji zmiennych i funkcji wewnątrz modułów, podobnie jak w przypadku używania publicznych i prywatnych deklaracji w Javie i C ++. Pliki źródłowe w C pełnią rolę modułów. Każda zmienna globalna lub funkcja zadeklarowana z atrybutem statycznym jest prywatna dla tego modułu. Podobnie każda zmienna globalna lub funkcja zadeklarowana bez atrybutu statycznego jest publiczna i może być dostępna dla każdego innego modułu. Dobrą praktyką programistyczną jest ochrona zmiennych i funkcji za pomocą atrybutu statycznego, gdy tylko jest to możliwe.
odpowiedź pmg jest bardzo przekonująca. Jeśli chcesz wiedzieć, jak działają deklaracje statyczne na poziomie obiektu, poniższe informacje mogą być dla Ciebie interesujące. Ponownie użyłem tego samego programu napisanego przez pmg i skompilowałem go do pliku .so (obiekt współdzielony)
Następująca zawartość jest po zrzuceniu pliku .so do czegoś czytelnego dla człowieka
0000000000000675 f1 : adres funkcji f1
000000000000068c f2 : adres funkcji f2 (staticc)
zwróć uwagę na różnicę w adresie funkcji, to coś znaczy. W przypadku funkcji zadeklarowanej z innym adresem może to bardzo dobrze oznaczać, że f2 znajduje się bardzo daleko lub w innym segmencie pliku obiektowego.
Linkery używają czegoś, co nazywa się PLT (tablica powiązań procedur) i GOT (tablica globalnych przesunięć), aby zrozumieć symbole, do których mają dostęp.
Na razie pomyśl, że GOT i PLT w magiczny sposób wiążą wszystkie adresy, a sekcja dynamiczna zawiera informacje o wszystkich tych funkcjach, które są widoczne dla linkera.
Po zrzuceniu dynamicznej sekcji pliku .so otrzymujemy kilka wpisów, ale interesują nas tylko funkcje f1 i f2 .
Sekcja dynamiczna zawiera tylko wpis dla funkcji f1 pod adresem 0000000000000675, a nie dla f2 !
Num: Wartość Rozmiar Typ Bind Vis Ndx Nazwa
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
I to wszystko !. Z tego jasno wynika, że linkerowi nie powiedzie się znalezienie funkcji f2, ponieważ nie ma jej w sekcji dynamicznej pliku .so.
Gdy zajdzie potrzeba ograniczenia dostępu do niektórych funkcji, podczas definiowania i deklarowania funkcji użyjemy słowa kluczowego 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 */