Co to jest funkcja „statyczna” w C?

505

Pytanie dotyczyło zwykłego funkcje, a nie static metody, jak wyjaśniono w komentarzach.

Rozumiem, czym jest staticzmienna, ale czym jest staticfunkcja?

I dlaczego jest tak, że jeśli zadeklaruję funkcję, powiedzmy void print_matrix, powiedzmy a.c(BEZ a.h) i dołączę "a.c"- rozumiem "print_matrix@@....) already defined in a.obj", ALE jeśli zadeklaruję ją tak, jak się static void print_matrixwtedy kompiluje?

AKTUALIZACJA Żeby wszystko wyjaśnić - wiem, że włączenie .cjest złe, jak wielu z was zauważyło. Po prostu robię to, aby tymczasowo zwolnić miejsce, main.cdopóki nie będę miał lepszego pojęcia, jak zgrupować wszystkie te funkcje w odpowiednie .hi .cpliki. Tylko tymczasowe, szybkie rozwiązanie.

Slava V.
źródło

Odpowiedzi:

684

staticFunkcje są funkcjami, które są widoczne tylko dla innych funkcji w tym samym pliku (a dokładniej tej samej jednostce tłumaczącej ).

EDYCJA : Dla tych, którzy myśleli, że autor pytań oznaczał „metodę klasową”: Gdy pytanie jest oznaczone C, oznacza zwykłą starą funkcję C. W przypadku metod klasy (C ++ / Java / ...) staticoznacza to, że tę metodę można wywołać na samej klasie, bez konieczności wystąpienia tej klasy.

Johannes Weiss
źródło
2
Właściwie nie oznaczyłem go c ++, prawdopodobnie niektórzy administratorzy, ale chodziło o C ++, więc jaka jest różnica w C ++?
Slava V
16
Metody C ++ są często nazywane „funkcjami składowymi”, więc zgadzam się, że C ++ wprowadza nieco niejasności. To nie twoja wina - język używa słowa kluczowego do dwóch różnych rzeczy.
Chuck
2
Nie, on wciąż oznacza funkcję C ++. Funkcja wolna od C ++ niż funkcja składowa C ++.
Wyścigi lekkości na orbicie
3
@Chuck: terminologia C ++ nigdy nie używa słowa „metoda”; taka jest terminologia Java - w standardowych dokumentach C ++ jest ona zawsze nazywana „funkcją członka” (patrz ta odpowiedź lub ten słownik C ++ vs terminy Java (np. C ++ używa „członka danych”, a Java używa „pola” itp.)).
ShreevatsaR
6
Aby wyjaśnić nieco tę odpowiedź: nazwa funkcji jest widoczna tylko dla innych części tej samej jednostki tłumaczeniowej, poniżej pierwszej deklaracji tej nazwy. Funkcja może być wywołana z innych jednostek (i wcześniejszych części tej samej jednostki) za pomocą innych środków, np. Wskaźnika funkcji.
MM
199

Istnieje duża różnica między funkcjami statycznymi w C a statycznymi funkcjami członka w C ++. W C funkcja statyczna nie jest widoczna poza jednostką tłumaczącą, która jest plikiem obiektowym, w którym jest kompilowana. Innymi słowy, uczynienie funkcji statyczną ogranicza jej zasięg. Możesz uznać funkcję statyczną za „prywatną” dla jej pliku * .c (chociaż nie jest to ściśle poprawne).

W C ++ „statyczny” może również odnosić się do funkcji składowych i członków danych klas. Statyczny element danych jest również nazywany „zmienną klasy”, podczas gdy niestatyczny element danych jest „zmienną instancji”. To terminologia Smalltalk. Oznacza to, że istnieje tylko jedna kopia statycznego elementu danych współużytkowanego przez wszystkie obiekty klasy, podczas gdy każdy obiekt ma własną kopię niestatycznego elementu danych. Zatem element danych statycznych jest zasadniczo zmienną globalną, która jest członkiem klasy.

Niestatyczne funkcje składowe mogą uzyskać dostęp do wszystkich elementów danych klasy: statycznych i niestatycznych. Funkcje elementów statycznych mogą działać tylko na elementach danych statycznych.

Jednym ze sposobów myślenia o tym jest to, że w C ++ elementy danych statycznych i funkcje elementów statycznych nie należą do żadnego obiektu, ale do całej klasy.

Dima
źródło
42
C ++ ma również plik statyczny. Nie ma potrzeby angażowania w to C.
Wyścigi lekkości na orbicie
17
W C ++ funkcja statyczna jest funkcją statyczną. Funkcja członka statycznego to funkcja członka statycznego, znana również jako metoda. Fakt, że C nie ma członków, nie oznacza, że ​​funkcje to „C”.
Gerasimos R
3
czy jest jakaś różnica między globalnym zmiennym a zmiennym statycznym klasowym (z wyjątkiem przestrzeni nazw)?
Alexander Malakhov
3
Przestrzeń nazw to główna różnica. Inną różnicą jest to, że możesz ustawić prywatny element danych jako prywatny, a zatem dostępny tylko z funkcji członka klasy. Innymi słowy, masz znacznie większą kontrolę nad statycznym elementem danych w porównaniu do zmiennej globalnej.
Dima,
2
Czy ktoś mógłby wyjaśnić, dlaczego myślenie o funkcji statycznej jako prywatnej w pliku .c nie jest ściśle poprawne? Co pozostało do powiedzenia?
YoTengoUnLCD
77

Istnieją dwa zastosowania słowa kluczowego static, jeśli chodzi o funkcje w C ++.

Pierwszym jest oznaczenie funkcji jako wewnętrznej powiązania, aby nie można było odwoływać się do niej w innych jednostkach tłumaczeniowych. To użycie jest przestarzałe w C ++. Dla tego zastosowania preferowane są nienazwane przestrzenie nazw.

// inside some .cpp file:

static void foo();    // old "C" way of having internal linkage

// C++ way:
namespace
{
   void this_function_has_internal_linkage()
   {
      // ...
   }
}

Drugie użycie jest w kontekście klasy. Jeśli klasa ma statyczną funkcję członka, oznacza to, że funkcja jest członkiem klasy (i ma zwykły dostęp do innych członków), ale nie musi być wywoływana przez określony obiekt. Innymi słowy, wewnątrz tej funkcji nie ma wskaźnika „ten”.

Brian Neal
źródło
1
Pytanie dotyczy statycznego w c.
Deqing
8
@Deqing pytanie było pierwotnie oznaczone C ++, a autor mówi o używaniu plików „.cpp”.
Brian Neal
57

Przykład minimalnego zakresu wielu plików do uruchomienia

Tutaj ilustruję, jak staticwpływa na zakres definicji funkcji w wielu plikach.

ac

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * /programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
 */
/*void f() { puts("a f"); }*/

/* OK: only declared, not defined. Will use the one in main. */
void f(void);

/* OK: only visible to this file. */
static void sf() { puts("a sf"); }

void a() {
    f();
    sf();
}

main.c

#include <stdio.h>

void a(void);        

void f() { puts("main f"); }

static void sf() { puts("main sf"); }

void m() {
    f();
    sf();
}

int main() {
    m();
    a();
    return 0;
}

GitHub w górę .

Skompiluj i uruchom:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main

Wynik:

main f
main sf
main f
a sf

Interpretacja

  • istnieją dwie osobne funkcje sf, po jednej dla każdego pliku
  • jest jedna wspólna funkcja f

Jak zwykle, im mniejszy zakres, tym lepiej, więc zawsze deklaruj funkcje, staticjeśli możesz.

W programowaniu C pliki są często używane do reprezentowania „klas”, a staticfunkcje reprezentują „prywatne” metody klasy.

Powszechnym wzorcem C jest przekazywanie thisstruktury jako pierwszego argumentu „metody”, który jest w zasadzie tym, co C ++ robi pod maską.

Co mówią o tym standardy

C99 N1256 wersja robocza 6.7.1 „ Specyfikatory klasy pamięci” mówią, że staticjest to „specyfikator klasy pamięci”.

6.2.2 / 3 „Powiązania identyfikatorów” mówi staticzakłada internal linkage:

Jeśli deklaracja identyfikatora zakresu pliku dla obiektu lub funkcji zawiera statyczny specyfikator klasy pamięci, identyfikator ma wewnętrzne powiązanie.

a 6.2.2 / 2 mówi, że internal linkagezachowuje się jak w naszym przykładzie:

W zestawie jednostek tłumaczeniowych i bibliotek, które stanowią cały program, każda deklaracja określonego identyfikatora z zewnętrznym powiązaniem oznacza ten sam obiekt lub funkcję. W obrębie jednej jednostki tłumaczeniowej każda deklaracja identyfikatora z wewnętrznym powiązaniem oznacza ten sam obiekt lub funkcję.

gdzie „jednostka tłumacząca” jest plikiem źródłowym po wstępnym przetwarzaniu.

Jak GCC implementuje to dla ELF (Linux)?

Z STB_LOCALwiązaniem.

Jeśli skompilujemy:

int f() { return 0; }
static int sf() { return 0; }

i zdemontować tabelę symboli za pomocą:

readelf -s main.o

wyjście zawiera:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 000000000000000b    11 FUNC    LOCAL  DEFAULT    1 sf
  9: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

więc wiązanie jest jedyną znaczącą różnicą między nimi. Valuejest tylko ich przesunięciem do .bsssekcji, więc spodziewamy się, że będzie się różnić.

STB_LOCALjest udokumentowany w specyfikacji ELF pod adresem http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :

STB_LOCAL Symbole lokalne nie są widoczne poza plikiem obiektowym zawierającym ich definicję. Lokalne symbole o tej samej nazwie mogą istnieć w wielu plikach bez kolidowania ze sobą

co czyni go idealnym wyborem do reprezentowania static.

Funkcje bez statyczne są STB_GLOBAL, a specyfikacja mówi:

Gdy edytor łączy łączy kilka plików obiektów relokowalnych, nie pozwala na wiele definicji symboli STB_GLOBAL o tej samej nazwie.

co jest spójne z błędami łącza w wielu definicjach niestatycznych.

Jeśli zwiększymy optymalizację za pomocą -O3, sfsymbol zostanie całkowicie usunięty z tabeli symboli: i tak nie można go używać z zewnątrz. TODO po co w ogóle utrzymywać funkcje statyczne na tablicy symboli, gdy nie ma optymalizacji? Czy można ich używać do czegokolwiek?

Zobacz też

Anonimowe przestrzenie nazw C ++

W C ++ możesz użyć anonimowych przestrzeni nazw zamiast statycznych, co daje podobny efekt, ale dodatkowo ukrywa definicje typów: Bezimienne / anonimowe przestrzenie nazw vs. funkcje statyczne

Ciro Santilli
źródło
3
Uwaga: void f() { puts("sf"); }(tj. dwie definicje f()) powoduje niezdefiniowane zachowanie bez wymaganej diagnostyki. Problem z jakością linkera polega na tym, że faktycznie pojawia się komunikat o błędzie.
MM
2
To jest najlepsze i precyzyjne wyjaśnienie! Niż Ty!
Aqua
20

Poniższe informacje dotyczą zwykłych funkcji C - w klasie C ++ modyfikator „static” ma inne znaczenie.

Jeśli masz tylko jeden plik, ten modyfikator nie ma absolutnie żadnej różnicy. Różnica polega na większych projektach z wieloma plikami:

W C każdy „moduł” (kombinacja sample.c i sample.h) jest kompilowany niezależnie, a następnie każdy z tych skompilowanych plików obiektowych (sample.o) jest łączony razem z plikiem wykonywalnym przez linker.

Załóżmy, że masz kilka plików, które dołączasz do pliku głównego, a dwa z nich mają funkcję używaną tylko wewnętrznie dla wygody, nazywaną add(int a, b)- kompilator łatwo utworzy pliki obiektowe dla tych dwóch modułów, ale linker zgłasza błąd, ponieważ znajduje dwie funkcje o tej samej nazwie i nie wie, z której powinien skorzystać (nawet jeśli nie ma nic do połączenia, ponieważ nie są one używane gdzie indziej, ale we własnym pliku).

Właśnie dlatego czynisz tę funkcję, która jest używana tylko wewnętrznie, funkcją statyczną. W takim przypadku kompilator nie tworzy typowej flagi „można połączyć to coś” dla linkera, dzięki czemu linker nie widzi tej funkcji i nie generuje błędu.

dersimn
źródło
16

Po pierwsze: umieszczenie .cpppliku w innym pliku jest na ogół złym pomysłem - prowadzi to do takich problemów :-) Normalnym sposobem jest utworzenie osobnych jednostek kompilacji i dodanie pliku nagłówka dla dołączonego pliku.

Po drugie:

C ++ ma tutaj mylącą terminologię - nie wiedziałem o tym, dopóki nie wskazałem tego w komentarzach.

a) static functions- odziedziczone po C i o czym tu mówisz. Poza każdą klasą. Statyczna funkcja oznacza, że nie są widoczne na zewnątrz obecna jednostka kompilacji - tak w twoim przypadku a.obj ma kopię a drugi kod ma niezależną kopię. (Nadęty końcowy plik wykonywalny z wieloma kopiami kodu).

b) static member function- co Object Orientation nazywa metodą statyczną . Mieszka w klasie. Nazywasz to klasą, a nie instancją obiektu.

Te dwie różne definicje funkcji statycznych są całkowicie różne. Uważaj - tu będą smoki.

Douglas Leeder
źródło
Cóż, robię to tylko po to, aby oczyścić trochę tymczasowo miejsca w main.cpp, dopóki nie zdecyduję, jak zorganizować plik w bibliotekach wraz z odpowiednimi plikami .hpp. Czy jest lepszy pomysł, jak to zrobić?
Slava V
1
Prawidłowa terminologia w C ++ to funkcja członka, a nie metoda. W języku C ++ nie ma „metod”. Metoda jest ogólnym terminem OO. C ++ implementuje je za pomocą funkcji członkowskich.
Brian Neal
14

definicje funkcji statycznych oznaczą ten symbol jako wewnętrzny. Nie będzie więc widoczny do łączenia z zewnątrz, a jedynie do funkcji w tej samej jednostce kompilacji, zwykle w tym samym pliku.

raimue
źródło
7

Funkcja statyczna to taka, którą można wywołać w samej klasie, w przeciwieństwie do instancji klasy.

Na przykład niestatyczna byłaby:

Person* tom = new Person();
tom->setName("Tom");

Ta metoda działa na instancji klasy, a nie na samej klasie. Możesz jednak mieć metodę statyczną, która może działać bez instancji. Jest to czasami używane we wzorcu fabrycznym:

Person* tom = Person::createNewPerson();
Papugi
źródło
2
Wydaje mi się, że mówisz o statycznej „metodzie”, a nie „funkcji”?
Slava V
Zakładałem, że masz na myśli funkcje statyczne w klasie.
Papugi
Gdybym wiedział, że „metody” w C ++ są nazywane „funkcjami metod”, byłbym bardziej zrozumiały. Cóż, teraz to robię :) W każdym razie dzięki
Slava V
5
W C ++ nie ma „metod”, tylko funkcje. Standard C ++ nigdy nie wspomina o „metodach”, tylko o „funkcjach”.
Brian Neal
1
@Puddle Wiem, co mówisz, ale w standardzie C ++ nie ma definicji „metody”. C ++ ma tylko różne funkcje. „Metoda” jest ogólnym terminem OO i jest używana w innych językach oraz nieformalnie w C ++. Metoda jest formalnie znana jako „funkcja składowa” w C ++.
Brian Neal
7

Drobna nit: funkcje statyczne są widoczne dla jednostki tłumaczeniowej, która w większości praktycznych przypadków jest plikiem, w którym funkcja jest zdefiniowana. Otrzymany błąd jest powszechnie określany jako naruszenie reguły One Definition.

Standard prawdopodobnie mówi coś takiego:

„Każdy program powinien zawierać dokładnie jedną definicję każdej funkcji lub obiektu nieliniowego używanego w tym programie; nie jest wymagana diagnostyka”.

Jest to sposób patrzenia na funkcje statyczne w języku C. Jest to jednak przestarzałe w C ++.

Dodatkowo w C ++ możesz zadeklarować funkcje składowe jako statyczne. Są to głównie metafunkcje, tzn. Nie opisują / nie modyfikują zachowania / stanu określonego obiektu, ale działają na całą klasę. Oznacza to również, że nie trzeba tworzyć obiektu, aby wywoływać funkcję członka statycznego. Ponadto oznacza to również, że dostęp do statycznych zmiennych składowych uzyskuje się tylko z poziomu takiej funkcji.

Dodałbym do przykładu Parrota wzorzec Singleton, który jest oparty na tego rodzaju statycznej funkcji składowej, aby uzyskać / użyć jednego obiektu przez cały czas istnienia programu.

bezpośrednio
źródło
7

Odpowiedź na funkcję statyczną zależy od języka:

1) W językach bez OOPS, takich jak C, oznacza to, że funkcja jest dostępna tylko w pliku, w którym jest zdefiniowana.

2) W językach z OOPS, takich jak C ++, oznacza to, że funkcję można wywołać bezpośrednio w klasie bez tworzenia jej instancji.

użytkownik2410022
źródło
To nie jest prawda. Wyjaśnienie drugiego akapitu odnosi się do „ statycznych funkcji składowych ” klasy, a nie do „ statycznych funkcji ”. W C ++ funkcja zakwalifikowana do staticma również zakres pliku, podobnie jak w C.
RobertS obsługuje Monikę Cellio
0

Ponieważ funkcja statyczna jest widoczna tylko w tym pliku. Właściwie kompilator może przeprowadzić dla ciebie optymalizację , jeśli zadeklarujesz „statyczny” dla jakiejś funkcji.

Oto prosty przykład.

main.c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

I kompiluj z

gcc -o main main.c

Zobaczysz, że to się nie udało. Ponieważ nawet nie implementujesz funkcji ghost ().

Ale co, jeśli użyjemy następującego polecenia.

gcc -DTEST -O2 -o main main.c

To sukces , a ten program można uruchomić normalnie.

Dlaczego? Istnieją 3 kluczowe punkty.

  1. -O2: Poziom optymalizacji kompilatora co najmniej 2.
  2. -DTEST: Zdefiniuj TEST, więc test () nie będzie wywoływany.
  3. Zdefiniowany „statyczny” do testowania ().

Tylko jeśli te 3 warunki są spełnione, możesz przejść kompilację. Z powodu tej „statycznej” deklaracji kompilator może potwierdzić, że test () NIGDY nie zostanie wywołany w innym pliku. Twój kompilator może usunąć test () podczas kompilacji. Ponieważ nie potrzebujemy test (), nie ma znaczenia, czy ghost () jest zdefiniowany czy wdrożony.

PoJyun Chiou
źródło
0

Czym jest staticfunkcja „ ”w C?

Zacznijmy od początku.

Wszystko opiera się na rzeczy zwanej „łączeniem”:

Identyfikator zadeklarowany w różnych zakresach lub w tym samym zakresie więcej niż jeden raz może odnosić się do tego samego obiektu lub funkcji w procesie zwanym łączeniem. 29) Istnieją trzy rodzaje powiązań: zewnętrzne, wewnętrzne i żadne ”.

Źródło: C18, 6.2.2 / 1


„W zestawie jednostek tłumaczeniowych i bibliotek, które tworzą cały program, każda deklaracja określonego identyfikatora z zewnętrznym powiązaniem oznacza ten sam obiekt lub funkcję. W ramach jednej jednostki tłumaczeniowej każda deklaracja identyfikatora z wewnętrznym powiązaniem oznacza ten sam obiekt lub funkcję Każda deklaracja identyfikatora bez powiązania oznacza unikalny byt. ”

Źródło: C18, 6.2.2 / 2


Jeśli funkcja jest zdefiniowana bez specyfikatora klasy pamięci, funkcja ma externdomyślnie wszystkie powiązania:

„Jeśli deklaracja identyfikatora funkcji nie ma specyfikatora klasy pamięci, jego powiązanie jest określane dokładnie tak, jakby było zadeklarowane za pomocą zewnętrznego specyfikatora klasy pamięci .”

Źródło: C18, 6.2.2 / 5

Oznacza to, że - jeśli twój program zawiera kilka jednostek tłumaczeniowych / plików źródłowych ( .club .cpp) - funkcja jest widoczna we wszystkich jednostkach tłumaczeniowych / plikach źródłowych, które posiada Twój program.

W niektórych przypadkach może to stanowić problem. Co jeśli chcesz użyć np. Dwóch różnych funkcji (definicji), ale o tej samej nazwie funkcji w dwóch różnych kontekstach (w rzeczywistości w kontekście pliku).

W C i C ++ statickwalifikator klasy pamięci zastosowany do funkcji w zakresie pliku (nie jest statyczną funkcją składową klasy w C ++ lub funkcją w innym bloku) teraz pomaga i oznacza, że ​​odpowiednia funkcja jest widoczna tylko wewnątrz jednostka tłumacząca / plik źródłowy, w którym została zdefiniowana, a nie w innych jednostkach TLU / plikach.

„Jeśli deklaracja identyfikatora zakresu pliku dla obiektu lub funkcji zawiera statyczny specyfikator klasy pamięci , identyfikator ma wewnętrzne powiązanie. 30)”


30) Deklaracja funkcji może zawierać specyfikator klasy pamięci wyłącznie, jeśli jest objęty zakresem pliku; patrz 6.7.1.

Źródło: C18, 6.2.2 / 3


Zatem staticfunkcja A ma sens, iff:

  1. Twój program zawiera kilka jednostek tłumaczeniowych / plików źródłowych ( .club .cpp).

    i

  2. Chcesz ograniczyć zakres funkcji do pliku, w którym zdefiniowano określoną funkcję.

Jeśli oba te wymagania nie są zgodne, nie musisz zawracać sobie głowy kwalifikowaniem funkcji jako static.


Notatki dodatkowe:

RobertS obsługuje Monikę Cellio
źródło