Widziałem słowo static
używane w różnych miejscach w kodzie C; czy to jest jak statyczna funkcja / klasa w C # (gdzie implementacja jest współdzielona między obiektami)?
Widziałem słowo static
używane w różnych miejscach w kodzie C; czy to jest jak statyczna funkcja / klasa w C # (gdzie implementacja jest współdzielona między obiektami)?
Odpowiedzi:
(1) jest bardziej zagranicznym tematem, jeśli jesteś nowicjuszem, więc oto przykład:
#include <stdio.h>
void foo()
{
int a = 10;
static int sa = 10;
a += 5;
sa += 5;
printf("a = %d, sa = %d\n", a, sa);
}
int main()
{
int i;
for (i = 0; i < 10; ++i)
foo();
}
To drukuje:
a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60
Jest to przydatne w przypadkach, gdy funkcja musi zachować pewien stan między wywołaniami, a nie chcesz używać zmiennych globalnych. Uważaj jednak, ta funkcja powinna być używana bardzo oszczędnie - sprawia, że twój kod nie jest bezpieczny dla wątków i trudniejszy do zrozumienia.
(2) Jest szeroko stosowany jako funkcja „kontroli dostępu”. Jeśli masz plik .c implementujący pewne funkcje, zwykle udostępnia użytkownikom tylko kilka funkcji „publicznych”. Reszta funkcji powinna zostać wykonana static
, aby użytkownik nie mógł uzyskać do nich dostępu. To jest kapsułkowanie, dobra praktyka.
Cytując Wikipedię :
W języku programowania C statyczny jest używany ze zmiennymi globalnymi i funkcjami w celu ustawienia ich zakresu na plik zawierający. W zmiennych lokalnych statyczny służy do przechowywania zmiennej w pamięci przydzielonej statycznie zamiast w pamięci przydzielanej automatycznie. Chociaż język nie dyktuje implementacji żadnego rodzaju pamięci, pamięć przydzielona statycznie jest zwykle zarezerwowana w segmencie danych programu w czasie kompilacji, podczas gdy pamięć przydzielana automatycznie jest zwykle implementowana jako stos wywołań przejściowych.
Aby odpowiedzieć na twoje drugie pytanie, to nie jest tak jak w C #.
Jednak w C ++ static
służy również do definiowania atrybutów klas (współdzielonych przez wszystkie obiekty tej samej klasy) i metod. W C nie ma klas, więc ta funkcja jest nieistotna.
.c
i kilka plików nagłówkowych, ale diabeł zawsze jest w tym, co nie jest typowe.
Jest jeszcze jedno zastosowanie, które nie zostało tutaj omówione, i które jest częścią deklaracji typu tablicy jako argumentu funkcji:
int someFunction(char arg[static 10])
{
...
}
W tym kontekście oznacza to, że argumenty przekazywane do tej funkcji muszą być tablicą typu char
z co najmniej 10 elementami. Aby uzyskać więcej informacji, zobacz moje pytanie tutaj .
arg[0]
poprzez aby arg[9]
mieć wartości (co oznacza również, że funkcja nie przyjmuje wskaźnik NULL). Kompilatory mogą w jakiś sposób wykorzystać te informacje do optymalizacji, a analizatory statyczne mogą wykorzystać te informacje, aby upewnić się, że funkcja nigdy nie otrzyma wskaźnika zerowego (lub jeśli może powiedzieć, tablicę z mniejszą liczbą elementów niż podano).
static
w C99. Ma ponad półtorej dekady, ale nie wszyscy autorzy kompilatorów przyjęli wszystkie funkcje C99 - więc C99 jako całość pozostaje w dużej mierze nieznana.
int arr[n];
, to jest to VLA (tablica o zmiennej długości) , która została dodana w C99. Czy o to ci chodziło?
Krótka odpowiedź ... to zależy.
Zmienne lokalne zdefiniowane statycznie nie tracą swojej wartości między wywołaniami funkcji. Innymi słowy, są to zmienne globalne, ale mają zakres funkcji lokalnej, w której są zdefiniowane.
Statyczne zmienne globalne nie są widoczne poza plikiem C, w którym są zdefiniowane.
Funkcje statyczne nie są widoczne poza plikiem C, w którym są zdefiniowane.
private
w C, twoja analogia jest dobra: statyczna czyni rzeczy „prywatnymi” dla danego pliku. Pliki w C często odwzorowują klasy w C ++.
Przykład wielu zakresów zmiennych
Tutaj ilustruję, jak statyczny wpł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
*/
/*int i = 0;*/
/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/
/* OK: extern. Will use the one in main. */
extern int i;
/* OK: only visible to this file. */
static int si = 0;
void a() {
i++;
si++;
puts("a()");
printf("i = %d\n", i);
printf("si = %d\n", si);
puts("");
}
main.c
#include <stdio.h>
int i = 0;
static int si = 0;
void a();
void m() {
i++;
si++;
puts("m()");
printf("i = %d\n", i);
printf("si = %d\n", si);
puts("");
}
int main() {
m();
m();
a();
a();
return 0;
}
Skompiluj i uruchom:
gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
Wynik:
m()
i = 1
si = 1
m()
i = 2
si = 2
a()
i = 3
si = 1
a()
i = 4
si = 2
Interpretacja
si
, po jednej dla każdego plikui
Jak zwykle, im mniejszy zakres, tym lepiej, więc zawsze deklaruj zmienne, static
jeśli możesz.
W programowaniu C pliki są często używane do reprezentowania „klas”, a static
zmienne reprezentują prywatnych statycznych członków klasy.
Co mówią o tym standardy
C99 N1256 wersja robocza 6.7.1 „ Specyfikatory klasy pamięci” mówią, że static
jest to „specyfikator klasy pamięci”.
6.2.2 / 3 „Powiązania identyfikatorów” mówi static
zakł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 linkage
zachowuje 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_LOCAL
wiązaniem.
Jeśli skompilujemy:
int i = 0;
static int si = 0;
i zdemontować tabelę symboli za pomocą:
readelf -s main.o
wyjście zawiera:
Num: Value Size Type Bind Vis Ndx Name
5: 0000000000000004 4 OBJECT LOCAL DEFAULT 4 si
10: 0000000000000000 4 OBJECT GLOBAL DEFAULT 4 i
więc wiązanie jest jedyną znaczącą różnicą między nimi. Value
jest tylko ich przesunięciem do .bss
sekcji, więc spodziewamy się, że będzie się różnić.
STB_LOCAL
jest 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
.
Zmienne bez wartości statycznych 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
, si
symbol 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 przechowywać zmienne statyczne w tabeli symboli, gdy nie ma optymalizacji? Czy można ich używać do czegokolwiek? Może do debugowania.
Zobacz też
static
funkcji: https://stackoverflow.com/a/30319812/895245static
z extern
, co robi „odwrotnie”: Jak korzystać z extern do udostępniania zmiennych między plikami źródłowymi?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
To zależy:
int foo()
{
static int x;
return ++x;
}
Funkcja zwróci 1, 2, 3 itd. --- zmiennej nie ma na stosie.
static int foo()
{
}
Oznacza to, że ta funkcja ma zakres tylko w tym pliku. Zatem ac i bc mogą mieć różne foo()
s, a foo nie jest narażone na współdzielone obiekty. Więc jeśli zdefiniowałeś foo in ac, nie możesz uzyskać do niego dostępu z b.c
lub z innych miejsc.
W większości bibliotek C wszystkie „prywatne” funkcje są statyczne, a większość „publicznych” nie.
Ludzie ciągle mówią, że „statyczny” w C ma dwa znaczenia. Oferuję alternatywny sposób oglądania, który nadaje mu jedno znaczenie:
Wydaje się, że ma to dwa znaczenia, ponieważ w C każdy element, do którego można zastosować „statyczny”, ma już jedną z tych dwóch właściwości , więc wydaje się , że to konkretne użycie dotyczy tylko drugiej.
Rozważmy na przykład zmienne. Zmienne zadeklarowane poza funkcjami mają już trwałość (w segmencie danych), więc zastosowanie parametru „static” może sprawić, że nie będą widoczne poza bieżącym zakresem (jednostka kompilacji). Przeciwnie, zmienne zadeklarowane wewnątrz funkcji już nie są widoczne poza bieżącym zakresem (funkcją), więc zastosowanie „statycznego” może tylko je utrwalić.
Zastosowanie „statycznego” do funkcji jest tak samo jak zastosowanie go do zmiennych globalnych - kod jest koniecznie trwały (przynajmniej w języku), więc można zmienić tylko widoczność.
UWAGA: Te komentarze dotyczą tylko C. W C ++ zastosowanie „statycznego” do metod klasowych naprawdę nadaje słowu kluczowemu inne znaczenie. Podobnie w przypadku rozszerzenia argument-tablica C99.
static
daje wewnętrzne powiązanie z identyfikatorem.
Z Wikipedii:
W języku programowania C statyczny jest używany ze zmiennymi globalnymi i funkcjami w celu ustawienia ich zakresu na plik zawierający. W zmiennych lokalnych statyczny służy do przechowywania zmiennej w pamięci przydzielonej statycznie zamiast w pamięci przydzielanej automatycznie. Chociaż język nie dyktuje implementacji żadnego rodzaju pamięci, pamięć przydzielona statycznie jest zwykle zarezerwowana w segmencie danych programu w czasie kompilacji, podczas gdy pamięć przydzielana automatycznie jest zwykle implementowana jako stos wywołań przejściowych.
static
oznacza różne rzeczy w różnych kontekstach.
Możesz zadeklarować zmienną statyczną w funkcji C. Ta zmienna jest widoczna tylko w funkcji, jednak zachowuje się jak globalna, ponieważ jest inicjowana tylko raz i zachowuje swoją wartość. W tym przykładzie za każdym razem, foo()
gdy zadzwonisz , wydrukuje rosnący numer. Zmienna statyczna jest inicjowana tylko raz.
void foo ()
{
static int i = 0;
printf("%d", i); i++
}
Innym zastosowaniem statycznego jest zaimplementowanie funkcji lub zmiennej globalnej w pliku .c, ale nie chcesz, aby jej symbol był widoczny poza .obj
generowanym przez plik. na przykład
static void foo() { ... }
Jeśli zadeklarujesz zmienną w funkcji statycznej, jej wartość nie zostanie zapisana na stosie wywołań funkcji i będzie nadal dostępna po ponownym wywołaniu funkcji.
Jeśli zadeklarujesz zmienną globalną statyczną, jej zakres będzie ograniczony do pliku, w którym ją zadeklarowałeś. Jest to nieco bezpieczniejsze niż zwykłe globalne, które można czytać i modyfikować w całym programie.
Nienawidzę odpowiadać na stare pytanie, ale nie sądzę, żeby ktokolwiek wspomniał, jak K&R wyjaśnia to w sekcji A4.1 „The C Programming Language”.
Krótko mówiąc, słowo statyczne ma dwa znaczenia:
static
słowo kluczowe (duży nacisk na użycie go w kodzie jako słowo kluczowe) jest używane z deklaracją, daje to wewnętrznemu powiązaniu obiektu, więc można go używać tylko w obrębie tej jednostki tłumaczeniowej. Ale jeśli słowo kluczowe jest używane w funkcji, zmienia klasę pamięci obiektu (obiekt i tak byłby widoczny tylko w tej funkcji). Przeciwieństwem statycznego jest extern
słowo kluczowe, które daje obiektowi zewnętrzne powiązanie.Peter Van Der Linden nadaje te dwa znaczenia w „Expert C Programming”:
register
się specyfikator przechowywania klasy (C99 6.7.1 Przechowywanie klasy SPECYFIKATORY). Jest to coś więcej niż tylko wskazówka, na przykład nie można zastosować operatora adresu &
do obiektu z klasą pamięci, register
niezależnie od tego, czy kompilator przydzieli rejestr, czy nie.
W języku C statyczny ma dwa znaczenia, w zależności od zakresu jego zastosowania. W zakresie globalnym, gdy obiekt jest deklarowany na poziomie pliku, oznacza to, że obiekt ten jest widoczny tylko w tym pliku.
W każdym innym zakresie deklaruje obiekt, który zachowa swoją wartość między różnymi momentami wprowadzenia danego zakresu. Na przykład jeśli int jest odwzorowywany w ramach procedury:
void procedure(void)
{
static int i = 0;
i++;
}
wartość „i” jest inicjowana do zera przy pierwszym wywołaniu procedury, a wartość jest zachowywana przy każdym kolejnym wywołaniu procedury. wydrukowanie „i” spowoduje wygenerowanie sekwencji 0, 1, 2, 3, ...
Należy zauważyć, że zmienne statyczne w funkcjach są inicjalizowane przy pierwszym wejściu do tej funkcji i zachowują się nawet po zakończeniu ich wywołania; w przypadku funkcji rekurencyjnych zmienna statyczna jest inicjalizowana tylko raz i utrzymuje się również we wszystkich wywołaniach rekurencyjnych, a nawet po zakończeniu wywołania funkcji.
Jeśli zmienna została utworzona poza funkcją, oznacza to, że programista może używać zmiennej tylko w pliku źródłowym, zmienna została zadeklarowana.
Jeśli zadeklarujesz to w mytest.c
pliku:
static int my_variable;
Wówczas zmienną można zobaczyć tylko z tego pliku. Zmiennej nie można eksportować nigdzie indziej.
Jeśli zadeklarujesz wewnątrz funkcji, wartość zmiennej zachowa swoją wartość przy każdym wywołaniu funkcji.
Funkcji statycznej nie można wyeksportować spoza pliku. Tak więc w *.c
pliku ukrywasz funkcje i zmienne, jeśli zadeklarujesz je jako statyczne.
Zmienne statyczne w C mają okres istnienia programu.
Jeśli są zdefiniowane w funkcji, mają zasięg lokalny, tzn. Można uzyskać do nich dostęp tylko wewnątrz tych funkcji. Wartość zmiennych statycznych jest zachowywana między wywołaniami funkcji.
Na przykład:
void function()
{
static int var = 1;
var++;
printf("%d", var);
}
int main()
{
function(); // Call 1
function(); // Call 2
}
W powyższym programie var
jest przechowywany w segmencie danych. Jego żywotność to cały program C.
Po wywołaniu funkcji 1 var
staje się 2. Po wywołaniu funkcji 2 var
staje się 3.
Wartość var
nie jest niszczona między wywołaniami funkcji.
Gdyby var
miał zmienną niestatyczną i lokalną, byłby przechowywany w segmencie stosu w programie C. Ponieważ ramka stosu funkcji jest niszczona po powrocie funkcji, wartość parametru var
jest również niszczona.
Zainicjowane zmienne statyczne są przechowywane w segmencie danych programu C, podczas gdy niezainicjowane zmienne są przechowywane w segmencie BSS.
Kolejna informacja o statyce: jeśli zmienna jest globalna i statyczna, ma czas życia programu C, ale ma zasięg pliku. Jest widoczny tylko w tym pliku.
Aby spróbować:
static int x;
int main()
{
printf("Accessing in same file%d", x):
}
extern int x;
func()
{
printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
}
run gcc -c file1.c
gcc -c file2.c
Teraz spróbuj połączyć je za pomocą:
gcc -o output file1.o file2.o
Dałoby to błąd linkera, ponieważ x ma zakres pliku file1.c, a linker nie byłby w stanie rozpoznać odwołania do zmiennej x użytej w pliku2.c.
Bibliografia:
static int var = 1;
zmienia wartości z powrotem na jedną za każdym razem
Zmienna statyczna jest specjalną zmienną, której można użyć w funkcji, i zapisuje dane między połączeniami i nie usuwa ich między wywołaniami. Na przykład:
void func(){
static int count; // If you don't declare its value, the value automatically initializes to zero
printf("%d, ", count);
++count;
}
void main(){
while(true){
func();
}
}
Wyjście:
0, 1, 2, 3, 4, 5, ...
printf("%d, ", count); count++;
`printf ("% d, ", count ++) (nie ma to znaczenia: P).
Istnieją 2 przypadki:
(1) Zadeklarowane zmienne lokalne static
: Przydzielone w segmencie danych zamiast stosu. Jego wartość zostaje zachowana po ponownym wywołaniu funkcji.
(2) Zadeklarowane zmienne globalne lub funkcje static
: Niewidoczne poza jednostką kompilacji (tzn. Są lokalnymi symbolami w tablicy symboli podczas łączenia).
Zmienne statyczne mają tę właściwość, że zachowują swoją wartość nawet po przekroczeniu zakresu! W związku z tym zmienne statyczne zachowują swoją poprzednią wartość w poprzednim zakresie i nie są ponownie inicjowane w nowym zakresie.
Spójrz na to na przykład - statyczna zmienna int pozostaje w pamięci podczas działania programu. Zmienna normalna lub automatyczna jest niszczona po zakończeniu wywołania funkcji, w której zmienna została zadeklarowana.
#include<stdio.h>
int fun()
{
static int count = 0;
count++;
return count;
}
int main()
{
printf("%d ", fun());
printf("%d ", fun());
return 0;
}
To wygeneruje: 1 2
Gdy 1 pozostaje w pamięci, ponieważ została uznana za statyczną
Zmienne statyczne (takie jak zmienne globalne) są inicjowane jako 0, jeśli nie są jawnie inicjowane. Na przykład w poniższym programie wartość x jest drukowana jako 0, podczas gdy wartość y jest czymś śmieciowym. Zobacz to po więcej szczegółów.
#include <stdio.h>
int main()
{
static int x;
int y;
printf("%d \n %d", x, y);
}
Spowoduje to wyświetlenie: 0 [some_garbage_value]
Są to główne, które znalazłem, ale które nie zostały wyjaśnione powyżej dla początkujących!
W programowaniu C static
jest zastrzeżonym słowem kluczowym, które kontroluje zarówno żywotność, jak i widoczność. Jeśli zadeklarujemy zmienną jako statyczną wewnątrz funkcji, będzie ona widoczna tylko w tej funkcji. W tym zastosowaniu czas życia tej zmiennej statycznej rozpocznie się, gdy wywołanie funkcji, i zniszczy się po wykonaniu tej funkcji. możesz zobaczyć następujący przykład:
#include<stdio.h>
int counterFunction()
{
static int count = 0;
count++;
return count;
}
int main()
{
printf("First Counter Output = %d\n", counterFunction());
printf("Second Counter Output = %d ", counterFunction());
return 0;
}
Powyższy program da nam ten wynik:
First Counter Output = 1
Second Counter Output = 1
Ponieważ jak tylko wywołamy funkcję, zainicjuje ona count = 0
. A kiedy wykonamy counterFunction
, zniszczy zmienną count.