Pytanie:
Koncepcja słowa kluczowego static z perspektywy osadzonego C
Electro Voyager
2019-07-08 17:13:53 UTC
view on stackexchange narkive permalink
  static volatile unsigned char PORTB @ 0x06;
 

To jest wiersz kodu w pliku nagłówkowym mikrokontrolera PIC.Operator @ jest używany do przechowywania wartości PORTB wewnątrz adresu 0x06 , który jest rejestrem wewnątrz kontrolera PIC reprezentującym PORTB.Do tego momentu mam jasny pomysł.

Ta linia jest zadeklarowana jako zmienna globalna w pliku nagłówkowym ( .h ).Tak więc z tego, co dowiedziałem się o języku C, „statyczna zmienna globalna” nie jest widoczna dla żadnego innego pliku - lub po prostu statyczne zmienne / funkcje globalne nie mogą być używane poza bieżącym plikiem.

Zatem w jaki sposób to słowo kluczowe PORTB może być widoczne w moim głównym pliku źródłowym i wielu innych plikach nagłówkowych, które utworzyłem ręcznie?

Do głównego pliku źródłowego dodałem tylko plik nagłówkowy #include pic.h Czy ma to coś wspólnego z moim pytaniem?

nie ma problemu z pytaniem, ale obawiam się, że zła sekcja SE
static jest zwykle używane wewnątrz funkcji, aby określić, że zmienna jest tworzona raz i zachowuje jej wartość od jednego wykonania funkcji do następnego.zmienna globalna to zmienna utworzona poza jakąkolwiek funkcją, dzięki czemu jest widoczna wszędzie.static global nie ma sensu.
@Finbarr Źle.Pliki globalne `static` są widoczne w całej pojedynczej jednostce kompilacji i nie są eksportowane poza nią.Są bardzo podobni do „prywatnych” członków klasy w OOP.To znaczy.każda zmienna, która musi być współdzielona między różnymi funkcjami wewnątrz jednostki kompilacji, ale nie powinna być widoczna poza tym c.u.* powinno * naprawdę być „statyczne”.Zmniejsza to również „stukanie” globalnej przestrzeni nazw programu.
Re * „Operator @ jest używany do przechowywania wartości PORTB wewnątrz adresu 0x06” *.Naprawdę?Czy nie jest bardziej jak * "Operator @ jest używany do przechowywania zmiennej" PORTB "pod bezwzględnym adresem pamięci 0x06" *?
Sześć odpowiedzi:
#1
+20
jonk
2019-07-08 23:56:47 UTC
view on stackexchange narkive permalink

Słowo kluczowe „statyczne” w języku C ma dwa zasadniczo różne znaczenia.

Ograniczenie zakresu

W tym kontekście „static” łączy się z „extern”, aby kontrolować zakres nazwy zmiennej lub funkcji. Static powoduje, że nazwa zmiennej lub funkcji jest dostępna tylko w jednej jednostce kompilacji i dostępna tylko dla kodu, który istnieje po deklaracji / definicji w tekście jednostki kompilacji.

To ograniczenie samo w sobie ma znaczenie tylko wtedy i tylko wtedy, gdy masz więcej niż jedną jednostkę kompilacji w swoim projekcie. Jeśli masz tylko jedną jednostkę kompilacji, nadal działa, ale te efekty są w większości bezcelowe (chyba że lubisz kopać w plikach obiektowych, aby przeczytać, co wygenerował kompilator).

Jak zauważono, to słowo kluczowe w tym kontekście łączy się ze słowem kluczowym „extern”, które działa odwrotnie - dzięki temu, że nazwa zmiennej lub funkcji jest połączona z tą samą nazwą, którą można znaleźć w innych jednostkach kompilacji. Możesz więc spojrzeć na „statyczny” jako wymagający, aby zmienna lub nazwa znajdowała się w bieżącej jednostce kompilacji, podczas gdy „extern” umożliwia łączenie jednostek cross-kompilacji.

Statyczny okres istnienia

Statyczny czas życia oznacza, że ​​zmienna istnieje przez cały czas trwania programu (nieważne, jak długo to trwa). Kiedy używasz „statycznej” do deklarowania / definiowania zmiennej w funkcji, oznacza to, że zmienna jest tworzona jakiś czas przed jej pierwsze użycie (co oznacza, że ​​za każdym razem, gdy tego doświadczyłem, zmienna jest tworzona przed uruchomieniem funkcji main ()) i nie jest później niszczona. Nawet wtedy, gdy wykonanie funkcji jest zakończone i powraca do wywołującego. I tak jak statyczne zmienne czasu życia zadeklarowane poza funkcjami, są one inicjowane w tym samym momencie - przed uruchomieniem funkcji main () - do semantycznego zera (jeśli nie podano inicjalizacji) lub do określonej jawnej wartości, jeśli została podana.

Różni się to od zmiennych funkcji typu „auto”, które są tworzone jako nowe (lub jakby nowe) za każdym razem, gdy funkcja jest wprowadzana, a następnie są niszczone (lub, jakby zostały zniszczone), gdy funkcja kończy działanie .

W przeciwieństwie do wpływu zastosowania „statycznej” na definicję zmiennej poza funkcją, co bezpośrednio wpływa na jej zakres, zadeklarowanie zmiennej funkcji (oczywiście w treści funkcji) jako „statyczna” ma nie wpływ na jego zakres. Zakres jest określony przez fakt, że został zdefiniowany w treści funkcji. Statyczne zmienne czasu życia zdefiniowane w funkcjach mają taki sam zakres, jak inne zmienne „auto” zdefiniowane w treściach funkcji - zakres funkcji.

Podsumowanie

Zatem słowo kluczowe „statyczne” ma różne konteksty, co oznacza „bardzo różne znaczenia”. Powodem, dla którego został użyty na dwa sposoby, jak ten, było unikanie używania innego słowa kluczowego. (Odbyła się długa dyskusja na ten temat.) Uznano, że programiści mogą tolerować użycie, a wartość unikania kolejnego słowa kluczowego w języku była ważniejsza (niż argumenty w innym przypadku).

(Wszystkie zmienne zadeklarowane poza funkcjami mają statyczny okres istnienia i nie potrzebują słowa kluczowego „static”, aby było to prawdą. Dzięki temu słowo kluczowe, które było tam używane, zostało zwolnione i oznaczało coś zupełnie innego: pojedyncza jednostka kompilacji. ”To swego rodzaju hack).

Szczegółowa uwaga

statyczny lotny bez znaku PORTB @ 0x06;

Słowo „statyczny” w tym miejscu należy interpretować w ten sposób, że konsolidator nie będzie próbował dopasować wielu wystąpień PORTB, które można znaleźć w więcej niż jednej jednostce kompilacji (zakładając, że kod ma więcej niż jedną). p>

Używa specjalnej (nieprzenośnej) składni do określenia "lokalizacji" (lub wartości numerycznej etykiety, która jest zwykle adresem) PORTB. Tak więc linker otrzymuje adres i nie musi go szukać. Gdybyś miał dwie jednostki kompilacji używające tej linii, i tak każda z nich wskazywałaby to samo miejsce. Nie ma więc potrzeby oznaczania go tutaj „extern”.

Gdyby użyli „extern”, mogłoby to stanowić problem. Konsolidator byłby wtedy w stanie zobaczyć (i próbowałby dopasować) wiele odniesień do PORTB znalezionych w wielu kompilacjach. Jeśli wszystkie z nich określają taki adres, a adresy z jakiegoś powodu NIE są takie same [błąd?], To co ma zrobić? Skarżyć się? Lub? (Technicznie rzecz biorąc, z „extern” zasadą kciuka byłoby, że tylko jednostka kompilacji ONE określałaby wartość, a pozostałe nie powinny).

Po prostu łatwiej jest oznaczyć go jako „statyczny”, dzięki czemu linker nie będzie martwił się konfliktami i po prostu obarczaj winą wszelkie błędy związane z błędnie dopasowanymi adresami tego, kto zmienił adres na taki, którym nie powinien. p>

Tak czy inaczej, zmienna jest traktowana jako posiadająca „statyczny okres istnienia”. (I „niestabilny”.)

Deklaracja nie jest definicją , ale wszystkie definicje są deklaracjami

W języku C definicja tworzy obiekt. To również deklaruje. Jednak deklaracja nie zwykle (zobacz notatka poniżej) tworzy obiekt.

Poniżej znajdują się definicje i deklaracje:

  static int a;
static int a = 7;
extern int b = 5;
extern int f () {return 10; }
 

Poniższe informacje nie są definicjami, a jedynie deklaracjami:

  extern int b;
extern int f ();
 

Zauważ, że deklaracje nie tworzą rzeczywistego obiektu. Deklarują tylko szczegóły na jego temat, których kompilator może następnie użyć, aby pomóc w wygenerowaniu poprawnego kodu oraz w razie potrzeby dostarczać ostrzeżenia i komunikaty o błędach.

  • Powyżej, radzę powiedzieć „zwykle”.W niektórych przypadkach deklaracja może utworzyć obiekt i dlatego jest promowana do definicji przez konsolidator (nigdy przez kompilator). Więc nawet w tym rzadkim przypadku kompilator C nadal uważa, że deklaracja jest tylko deklaracją.Jest to faza łącznika, która dokonuje niezbędnych promocji jakiejś deklaracji.Miej to na uwadze.

    Jeśli w powyższych przykładach okaże się, że istnieją deklaracje tylko dla "extern int b;"we wszystkich połączonych jednostkach kompilacji odpowiedzialność za utworzenie definicji ponosi konsolidator.Należy pamiętać, że jest to zdarzenie czasu łącza.Kompilator jest całkowicie nieświadomy podczas kompilacji.Można to określić tylko w czasie łącza, jeśli deklaracja tego typu jest najczęściej promowana.

    Kompilator jest świadomy, że „static int a;”nie mogą być promowane przez linkera w czasie łączenia, więc w rzeczywistości jest to definicja w czasie kompilacji .

Świetna odpowiedź, +1!Tylko jedna drobna uwaga: * mogli * użyć `extern` i byłby to bardziej właściwy sposób w C: * Zadeklarowanie * zmiennej` extern` w pliku nagłówkowym, aby była umieszczana wiele razy w programie i *zdefiniowanie * go w jakimś pliku bez nagłówka do skompilowania i dołączenia dokładnie raz.W końcu `PORTB` * ma * być dokładnie * jedną * instancją zmiennej, do której mogą się odnosić różne c.u.Tak więc użycie „static” tutaj jest swego rodzaju skrótem, który pozwolił uniknąć konieczności używania innego pliku .c oprócz pliku nagłówkowego.
Chciałbym również zauważyć, że zmienna statyczna zadeklarowana w funkcji * nie * jest zmieniana w trakcie wywołań funkcji, co może być przydatne dla funkcji, które muszą zachować pewne informacje o stanie (użyłem jej specjalnie do tego celu w przeszłości).
@Peter Myślę, że to powiedziałem.Ale może nie tak dobrze, jak byś chciał?
@JimmyB Nie, zamiast tego nie mogli użyć wyrażenia „extern” w przypadku deklaracji zmiennych funkcji, które zachowują się jak „static”.„extern” jest już opcją dla deklaracji zmiennych (nie definicji) w treściach funkcji i służy innemu celowi - zapewniając dostęp w czasie łączenia do zmiennych zdefiniowanych poza jakąkolwiek funkcją.Ale możliwe, że ja też źle zrozumiem twój punkt widzenia.
Połączenie @JimmyB Extern z pewnością byłoby możliwe, chociaż nie wiem, czy jest „bardziej właściwe”.Należy wziąć pod uwagę, że kompilator może być w stanie emitować bardziej zoptymalizowany kod, jeśli informacje zostaną znalezione w jednostce tłumaczącej.W przypadku scenariuszy osadzonych oszczędzanie cykli na każdej instrukcji we / wy może być dużym problemem.
Aby dodać: pamiętaj, że dyrektywa #include jest preprocesorem i może to rzeczywiście stworzyć wiele PORTB w całej kompilacji, jeśli nie zostanie ona przechwycona przez [include guards] (https://en.wikipedia.org/wiki/Include_guard).To liczy się jako wielokrotne wystąpienie.
@CortAmmon Myślę, że jest to "bardziej właściwy" sposób, ponieważ w tym przypadku "statyczny" sposób działa tylko dlatego, że opiera się na (nieprzenośnym) rozszerzeniu (`@ 0x06;`), które pozwala na przydzielenie wielu (statycznych) instancjizmiennej do jednego miejsca w pamięci.Używanie „extern” nie byłoby problemem.
@jonk Rzeczywiście wydaje mi się, że mnie źle zrozumiałeś.Zauważ, że nie mówimy o deklaracjach / definicjach zmiennych wewnątrz funkcji, ale o * globalnych *.Zwróć uwagę na inne znaczenie słowa „statyczny” dla zmiennych globalnych w przeciwieństwie do zmiennych lokalnych funkcji.
„Technicznie rzecz biorąc, przy 'extern' przyjęta reguła byłaby taka, że tylko JEDNA jednostka kompilacji określałaby wartość, a inne nie”.- Powinno to wyjaśnić, że „@ 0x06” jest częścią * definicji * zmiennej, a nie jej deklaracją.W programie może istnieć tylko jedna * definicja * zmiennej (niestatycznej), cokolwiek innego powoduje błąd w czasie łączenia (lub wcześniej).Możesz * zadeklarować * zmienną (niestatyczną) wiele razy za pomocą argumentu „extern”, ale może istnieć tylko * jedna * definicja.
@JimmyB Twoje komentarze zostawiam innym do interpretacji.Wcześniej napisałem kompilator C.Wiem nieco więcej niż wielu z tego doświadczenia.Ale nie jestem pewien, co mówisz.Jednak może to być czas, który muszę poświęcić na przeanalizowanie tego.Kiedy mam chwilę i ochotę, mogę spróbować to zrozumieć.Niezależnie od tego uważam, że to, co napisałem, jest zgodne z moimi doświadczeniami i żałuję, że nie podążyłem za waszymi motywacjami i komentarzami.
@JimmyB I tak, DOBRZE zdaję sobie sprawę z różnicy między deklaracjami a definicjami - zwłaszcza jeśli chodzi o zmienne globalne i zewnętrzne.Ponownie, nie rozumiem twojego problemu z moim pisaniem.Ale chciałbym poprawić to, co napisałem, co oznacza, że chciałbym zrozumieć twoją krytykę (jeśli tak jest).
Tak naprawdę nie chciałem krytykować :) I po ponownym przeczytaniu Twojej odpowiedzi zauważyłem, że naprawdę to Ty wskazałeś na różne znaczenia słowa „statyczny”.Chodzi mi tylko o to, do czego w tym przypadku jest używana statyczna deklaracja (i definicja) w pliku nagłówkowym, tj. Odwoływanie się do tej samej zmiennej / lokalizacji pamięci z wielu plików .c, działa * tylko * z powodu (nieprzenośnego,niestandardowy) `@ 0x06;` specyfikator adresu, podczas gdy "właściwym" sposobem w C byłoby użycie `extern` w deklaracjach i osobnej, pojedynczej * definicji * zmiennej.Bez specyfikatora adresu „@ 0x06”, every
@JimmyB Jeśli uważnie czytasz, powinieneś zobaczyć, gdzie powiem tylko to, co myślę, że mówisz.Przyjrzyj się bliżej do końca.Może trzeci akapit od dołu?
statyczna instancja zmiennej zostanie przydzielona do jej własnej, oddzielnej lokalizacji w pamięci, której unika argument „extern”.
Przeczytaj to jeszcze raz, nie zgadzam się z tym, że akapit zaczynający się od „Gdyby użyli 'extern', mógłby stanowić problem”.- `extern` nie stwarzałby problemu, ponieważ zapewnia, że istnieje tylko * jedna * definicja zmiennej.
@JimmyB Nie mówię, że „zewnętrzny” jest problemem.W rzeczywistości ty i ja dokładnie zgadzamy się, że to nie jest problem.Oczywiście pod warunkiem, że zdefiniujesz tylko raz.Mówię tam o tym, dlaczego autor mógł wybrać inną ścieżkę, używając zamiast tego „statycznego”.To wszystko.
#2
+9
JimmyB
2019-07-08 17:23:47 UTC
view on stackexchange narkive permalink

static s nie są widoczne poza bieżącą jednostką kompilacji , czyli „jednostką tłumaczeniową”.To nie to samo, co ten sam plik .

Zauważ, że dołączasz plik nagłówkowy do dowolnego pliku źródłowego, w którym mogą być potrzebne zmienne zadeklarowane w nagłówku.To włączenie sprawia, że plik nagłówkowy staje się częścią bieżącej jednostki tłumaczeniowej i (instancją) zmiennej widocznej w niej.

Dzięki za odpowiedź.„Compilation Unit”, Przepraszam, nie rozumiem, czy możesz wyjaśnić ten termin.Pozwól, że zadam ci jeszcze jedno pytanie, nawet jeśli chcemy użyć zmiennych i funkcji zapisanych w innym pliku, musimy najpierw ZAWIERAĆ ten plik w naszym głównym PLIKU ŹRÓDŁOWYM.Dlaczego więc słowo kluczowe „static volatile” w tym pliku nagłówkowym.
@ElectroVoyager https: // www.cs.auckland.ac.nz / referencje / unix / digital / AQTLTBTE / DOCU_015.HTM
Dość dogłębna dyskusja na https://stackoverflow.com/questions/572547/what-does-static-mean-in-c
Dziękuję za wyjaśnienie, ale nadal nie rozumiem, dlaczego statyczne słowo kluczowe jest przeznaczone dla zmiennych globalnych.Napisałem program, który zawierał plik nagłówkowy „includeer.h” w C ++.Wewnątrz tego pliku usunąłem static int var = 56 i statyczną funkcję void say_hello ().Po dołączeniu tego pliku do mojego głównego pliku źródłowego mogę używać zarówno funkcji, jak i zmiennej.Ale na forum mówi się tak: „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”.
@ElectroVoyager;jeśli umieścisz ten sam nagłówek zawierający deklarację statyczną w wielu plikach źródłowych c, każdy z tych plików będzie miał zmienną statyczną o tej samej nazwie, ale * nie są one tą samą zmienną *.
Z @JimmyB link: `Pliki zawarte za pomocą dyrektywy #include preprocesora stają się częścią jednostki kompilacji .` Kiedy dołączasz plik nagłówkowy (.h) do pliku .c, myśl o tym jak o wstawieniu zawartości nagłówka do pliku źródłowego, a teraz to jest twoja jednostka kompilacji.Jeśli zadeklarujesz tę statyczną zmienną lub funkcję w pliku .c, możesz ich użyć tylko w tym samym pliku, który na końcu będzie kolejną jednostką kompilacji.
Właściwym terminem jest _translation unit_, a nie „compilation unit”.Zobacz 5.1.1.1/1: „Plik źródłowy wraz ze wszystkimi nagłówkami i plikami źródłowymi zawartymi w dyrektywie wstępnego przetwarzania„ # include ”jest znany jako _przedprocesowa jednostka tłumaczeniowa_. Po wstępnym przetwarzaniu jednostka tłumacząca wstępnie przetwarzająca jest nazywana _ jednostką tłumaczeniową_.”
#3
+5
gustavovelascoh
2019-07-08 18:44:59 UTC
view on stackexchange narkive permalink

Spróbuję podsumować komentarze i odpowiedź @ JimmyB na wyjaśniającym przykładzie:

Załóżmy, że ten zestaw plików:

static_test.c:

  #include <stdio.h>
#if USE_STATIC == 1
    #include „static.h”
#jeszcze
    #include „no_static.h”
#endif

void var_add_one ();

void main () {

    Powiedz cześć();
    printf ("zmienna to% d \ n", zmienna);
    var_add_one ();
    printf ("teraz var to% d \ n", var);
}
 

static.h:

  static int var = 64;
static void say_hello () {
    printf ("Cześć !!! \ n");
};
 

no_static.h:

  int var = 64;
void say_hello () {
    printf ("Cześć !!! \ n");
};
 

static_src.c:

  #include <stdio.h>

#if USE_STATIC == 1
    #include „static.h”
#jeszcze
    #include „no_static.h”
#endif

void var_add_one () {
    var = var + 1;
    printf ("Dodano 1 do zmiennej:% d \ n", zmienna);
    Powiedz cześć();
}
 

Możesz skompilować i uruchomić kod za pomocą gcc -o static_test static_src.c static_test.c -DUSE_STATIC = 1; ./static_test do używania statycznego nagłówka lub gcc -o static_test static_src.c static_test.c -DUSE_STATIC = 0; ./static_test do używania niestatycznego nagłówka.

Zauważ, że obecne są tutaj dwie jednostki kompilacji: static_src i static_test. Jeśli używasz statycznej wersji nagłówka ( -DUSE_STATIC = 1 ), dla każdej jednostki kompilacji będą dostępne wersje var i say_hello , to znaczy, że obie jednostki mogą ich używać, ale sprawdź, czy mimo że funkcja var_add_one () zwiększa its var zmienna, gdy główna funkcja drukuje its var zmienna, wciąż jest 64:

  $ gcc -o static_test static_src.c static_test.c -DUSE_STATIC = 1; ./static_test 14:33: 12⌚
Dzień dobry!!!
var to 64
Dodano 1 do var: 65
Dzień dobry!!!
teraz var to 64
 

Teraz, jeśli spróbujesz skompilować i uruchomić kod, używając wersji niestatycznej ( -DUSE_STATIC = 0 ), zgłosi błąd łączenia z powodu zduplikowanej definicji zmiennej:

  $ gcc -o static_test static_src.c static_test.c -DUSE_STATIC = 0; ./static_test 14: 35: 30⌚
/tmp/ccLBy1s7.o:(.data+0x0): wielokrotna definicja `var '
/tmp/ccV6izKJ.o:(.data+0x0): pierwsza zdefiniowana tutaj
/tmp/ccLBy1s7.o: W funkcji `say_hello ':
static_test.c :(. text + 0x0): wielokrotna definicja słowa say_hello
/tmp/ccV6izKJ.o:static_src.c:(.text+0x0): pierwsza zdefiniowana tutaj
collect2: error: ld zwrócił 1 status wyjścia
zsh: nie ma takiego pliku lub katalogu: ./static_test
 

Mam nadzieję, że to pomoże Ci wyjaśnić tę sprawę.

#4
+4
Dmitry Grigoryev
2019-07-09 12:23:06 UTC
view on stackexchange narkive permalink

#include pic.h z grubsza oznacza „skopiuj zawartość pic.h do bieżącego pliku”. W rezultacie każdy plik zawierający pic.h otrzymuje własną lokalną definicję PORTB .

Być może zastanawiasz się, dlaczego nie ma jednej globalnej definicji PORTB . Powód jest dość prosty: zmienną globalną możesz zdefiniować tylko w jednym pliku C, więc jeśli chcesz używać PORTB w wielu plikach w swoim projekcie, potrzebujesz pic.h z deklaracją PORTB i pic.c z jego definicją . Pozwalanie każdemu plikowi C definiować własną kopię PORTB ułatwia tworzenie kodu, ponieważ nie musisz umieszczać w swoim projekcie plików, których nie napisałeś.

Dodatkową zaletą zmiennych statycznych w porównaniu do zmiennych globalnych jest mniejsza liczba konfliktów nazw. Plik C, który nie wykorzystuje żadnych funkcji sprzętowych MCU (a zatem nie zawiera pic.h ), może używać nazwy PORTB do własnych celów. Nie żeby było to dobrym pomysłem, ale kiedy tworzysz np. biblioteka matematyczna niezależna od MCU, byłbyś zaskoczony, jak łatwo jest przypadkowo ponownie użyć nazwy, która jest używana przez jeden z MCU.

* „zdziwiłbyś się, jak łatwo jest przypadkowo ponownie użyć nazwy używanej przez jeden z mikrokontrolerów” * - Odważę się mieć nadzieję, że wszystkie biblioteki matematyczne używają tylko małych liter, a wszystkie środowiska MCU używają tylko wielkich literdla nazw rejestrowych.
@vsz LAPACK dla jednego jest pełen historycznych nazw wielkimi literami.
#5
+3
Adam Haun
2019-07-10 02:12:08 UTC
view on stackexchange narkive permalink

Istnieje już kilka dobrych odpowiedzi, ale myślę, że przyczyną nieporozumień należy zająć się prosto i bezpośrednio:

Deklaracja PORTB nie jest standardem C. Jest to rozszerzenie języka programowania C, które działa tylko z kompilatorem PIC. Rozszerzenie jest potrzebne, ponieważ PIC nie zostały zaprojektowane do obsługi C.

Użycie słowa kluczowego static w tym miejscu jest mylące, ponieważ nigdy nie użyłbyś w normalnym kodzie static . W przypadku zmiennej globalnej użyłbyś extern w nagłówku, a nie static . Ale PORTB nie jest zwykłą zmienną . Jest to hack, który mówi kompilatorowi, aby używał specjalnych instrukcji asemblacji dla register IO. Zadeklarowanie PORTB static pomaga oszukać kompilator, aby zrobił właściwą rzecz.

static użyte w zakresie pliku ogranicza zakres zmiennej lub funkcji do tego pliku. „Plik” oznacza plik C i wszystko, co zostało do niego skopiowane przez preprocesor. Używając #include, kopiujesz kod do swojego pliku C. Dlatego użycie elementu static w nagłówku nie ma sensu - zamiast jednej zmiennej globalnej każdy plik, który # zawiera nagłówek, otrzyma oddzielną kopię zmiennej.

Wbrew powszechnemu przekonaniu statyczny zawsze oznacza to samo: statyczna alokacja z ograniczonym zakresem. Oto, co dzieje się ze zmiennymi przed i po zadeklarowaniu ich jako statyczne :

  + ------------------------ + ------------------ - + -------------------- +
| Typ / lokalizacja zmiennej | Alokacja | Zakres |
+ ------------------------ + ------------------- + ---- ---------------- +
| Normalny w pliku | statyczny | globalny |
| Normalne działanie | automatyczny (stos) | ograniczona (funkcja) |
| Statyczny w pliku | statyczny | ograniczona (plik) |
| Funkcja statyczna | statyczny | ograniczona (funkcja) |
+ ------------------------ + ------------------- + -------------------- +
 

Mylące jest to, że domyślne zachowanie zmiennych zależy od tego, gdzie są zdefiniowane.

#6
+2
user4574
2019-07-10 01:34:10 UTC
view on stackexchange narkive permalink

Przyczyną, dla której plik główny może zobaczyć „statyczną” definicję portu, jest dyrektywa #include. Ta dyrektywa jest równoważna wstawieniu całego pliku nagłówkowego do kodu źródłowego w tym samym wierszu, co sama dyrektywa.

Kompilator microchip XC8 traktuje pliki .c i .h dokładnie tak samo, więc możesz umieścić definicje zmiennych w jednym z nich.

Zwykle plik nagłówkowy zawiera odniesienie „extern” do zmiennych, które są zdefiniowane w innym miejscu (zwykle jest to plik .c).

Zmienne portu musiały być określone pod określonymi adresami pamięci, które pasują do rzeczywistego sprzętu. Więc rzeczywista (nie zewnętrzna) definicja musiała gdzieś istnieć.

Mogę się tylko domyślać, dlaczego firma Microchip zdecydowała się umieścić rzeczywiste definicje w pliku .h. Można przypuszczać, że chcieli tylko jednego pliku (.h) zamiast 2 (.h i .c) (aby ułatwić użytkownikowi).

Ale jeśli umieścisz rzeczywiste definicje zmiennych w pliku nagłówkowym, a następnie umieścisz ten nagłówek w wielu plikach źródłowych, konsolidator będzie narzekał, że zmienne są definiowane wiele razy.

Rozwiązaniem jest zadeklarowanie zmiennych jako statycznych, a następnie każda definicja jest traktowana jako lokalna dla tego pliku obiektowego, a konsolidator nie będzie narzekał.



To pytanie i odpowiedź zostało automatycznie przetłumaczone z języka angielskiego.Oryginalna treść jest dostępna na stackexchange, za co dziękujemy za licencję cc by-sa 4.0, w ramach której jest rozpowszechniana.
Loading...