Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Zmienna nieulotna w funkcji. [C]. Jak ją zadeklarować?

atom1477 14 Sty 2010 00:24 4607 56
  • #1 14 Sty 2010 00:24
    atom1477
    Poziom 43  

    Potrzebuję zmiennej lokalnej ale nie ulotnej.
    Mam kilkanaście procedur które filtrują sygnały i potrzeba mi kilkadziesiąt buforów próbek które oczywiście muszą być niezmienne aby kolejne wywołania funkcji filtrujących poprawnie aktualizowało mi wyjście filtrów.
    Na razie mam wszystkie zmienne jako globalne volatile ale robi się niezły bałagan.
    Każdy bufor chętnie wrzucił bym do każdej procedury, ale nie wiem jak zagwarantować aby te bufory były zachowywane pomiędzy wywołaniami funkcji.
    No i nie wiem: to ma być zmienna „volatile”, „static” czy „static volatile”.
    Ławo powiedzieć „sprawdź”. Ale to że raz mi zadziała to nie znaczy że tak jest dobrze. Bo może po prostu kompilator czegoś nie zoptymalizuje a później coś zmienię w programie i okaże się że to już nie będzie działało.
    Zresztą już miałem takie przypadki kiedy nie wiedząc jak coś zrobić musiałem sam do tego dojść. I kod zaczynał działać ale po wprowadzeniu zmian przestawał, bo po porostu było to zrobione niepoprawnie i działało tylko przypadkiem.
    Dlatego potrzebuję konkretnej odpowiedzi. Dużo się w necie naczytałem ale pewności nie mam (a stawiam na „static volatile”).
    Z góry dzięki za pomoc.

    0 29
  • Pomocny post
    #2 14 Sty 2010 00:43
    yogi009
    Poziom 42  

    Aż taki biegły w C nie jestem, ale bazując na innych językach, taką własność jak opisujesz (czyli zakres zmiennej jest ograniczony do ciała funkcji, ale po wyjściu z funkcji zmienna jest przechowywana w pamięci i może być ponownie użyta) mają zmienne statyczne.

    0
  • #3 14 Sty 2010 00:46
    atom1477
    Poziom 43  

    Dokładnie o to mi chodzi.
    Czyli "static"?

    0
  • Pomocny post
    #4 14 Sty 2010 00:54
    yogi009
    Poziom 42  

    Jeszcze raz zaznaczam, nie znam dokładnie C, ale w paru innych językach typ static wypełniał lukę między zmiennymi lokalnymi i globalnymi. Zmienna static była trochę "lokalna", bo poza funkcją jej nie widać, a trochę jakby "globalna", bo przechowywała wartość przez cały czas trawnia programu (no, trochę tu łopatologicznie poleciałem :) ).

    1
  • Pomocny post
    #5 14 Sty 2010 00:57
    mirekk36
    Poziom 42  

    nieulotna to volatile - czyli zmienna, która nie jest poddawana optymalizacji w odwołaniach do niej - i przydaje się głównie gdy są odwołania do tej zmiennej zarówno w przerwaniach i w programie głównym. Czyli jeśli z punktu widzenia programu głównego jest zmieniane niejawnie przez przerwanie. Wtedy volatile - ale nie dla wszystkich zmiennych globalnych - bo wtedy tracisz na jakości i w większości wypadków generuje się ciut więcej kodu i ciut wolniej działa to i owo

    static - w odniesieniu do zmiennych oznacza, że dla takiej zmiennej kompilator z góry rezerwuje komórkę pamięci RAM i jest ona dostępna dla danej funkcji czy także przerwania, w której(-ym) została zadeklarowana.

    Co ją różni od zwykłej zmiennej loklalnej w funkcji - ano to że zmienna lokalna tworzona jest na stosie przy wejściu do funkcji i kończy ona swój żywot przy wyjściu z funkcji bo niszczony jest ten fragment stosu.

    A zmienna static cały czas pozostaje w RAMie ale dostępna jest bez zmiany wartości przy kolejnych wywołaniach tej samej i tylko tej samej funkcji czy przerwania

    0
  • #6 14 Sty 2010 00:59
    atom1477
    Poziom 43  

    Ok. To chyba to. Dzięki.

    0
  • Pomocny post
    #7 14 Sty 2010 01:00
    mirekk36
    Poziom 42  

    a tak w ogóle to polecam ci zamiast tworzyć stada zmiennych globalnych - to nauczyć się dobrze korzystać ze struktur i unii do takich celów. Zobaczysz szybko jakie to kolejne ciekawe i super efekty za sobą niesie ;)

    a zmienne globalne o których piszesz - mam nadzieję że wiesz że są tylko globalne w ramach jednego pliku w którym mieści się kod. Aby były globalne czyli widoczne w innych plikach trzeba jeszcze porobić z nimi dodatkowe manewry i najlepiej przez pliki nagłówkowe *.h

    0
  • #8 14 Sty 2010 01:08
    atom1477
    Poziom 43  

    mirekk36 napisał:
    a tak w ogóle to polecam ci zamiast tworzyć stada zmiennych globalnych - to nauczyć się dobrze korzystać ze struktur i unii do takich celów. Zobaczysz szybko jakie to kolejne ciekawe i super efekty za sobą niesie ;)

    Wiem. Ale filtry są różne więc musiał bym i tak robić różne zmienne zamiast jednej struktury czy tablicy struktur. Dlatego wygodniejsze dla mnie są zmienne lokalne.
    A do tego to nie tylko filtry są. Filtry chciałem w pierwszej kolejności uporządkować, ale jeżeli już wiem jak to i całą resztę uporządkuję a do tego żadna struktura się nie nada bo każda zmienna jest inna.
    W ogóle to struktury i unie już dawno stosuję :D

    mirekk36 napisał:
    a zmienne globalne o których piszesz - mam nadzieję że wiesz że są tylko globalne w ramach jednego pliku w którym mieści się kod. Aby były globalne czyli widoczne w innych plikach trzeba jeszcze porobić z nimi dodatkowe manewry i najlepiej przez pliki nagłówkowe *.h

    To też wiem i dlatego jak na razie inne pliki źródłowe mam z rozszerzeniem .cc i dołączam je do głównego pliku źródłowego dyrektywami include. Mam pomysły, co?

    Tak więc dzięki za pomoc.

    0
  • Pomocny post
    #9 14 Sty 2010 01:13
    mirekk36
    Poziom 42  

    Nie - no oczywiście że nie chodziło mi o zastępowanie zmiennych loklanych zaraz strukturami globalnymi. Tylko teraz rozumiem że wcześniej po prostu nie wiedząc do końca o tym "static" - waliłeś wszystko jako globalne ;)

    ..... no masz masz "pomysły" jeśli inkludujesz pliki *.c - czego nie powinno się robić ;) ..... Program warto tak pisać i tylko tak żeby inkludować tylko i wyłącznie pliki *.h hmmm nawet nie to że warto - tak trzeba pisać

    0
  • #10 14 Sty 2010 01:24
    atom1477
    Poziom 43  

    Czy ja wiem. To nie jest błąd. Po prostu mam bardziej przejrzysty kod bo rozbity na kilka plików. A co w tym złego że potem je złączam zamiast osobno skompilować?
    To nawet lepiej że są łączone bo lepiej się je optymalizuje.
    To ma tylko wpływ na przejrzystość kodu po za tym że jest niezgodne z wytycznymi. Co to zmienia w działaniu programu? Chyba nic.

    Co do inkludowania to inkluduję jeszcze pliki .img czy .f.
    To są w zasadzie pliki .h tylko zmieniłem rozszerzenia żeby było wiadomo że siedzą w nich tablice stałych z obrazkami albo z czcionką. To też taki wielki błąd? :D

    Dodano po 2 [minuty]:

    Acha. Bo może nie zrozumiałeś. Ja nie inkluduję plików c tylko cc.
    Plik c nie zainkluduje się bo kompilator wywali warninga.

    0
  • Pomocny post
    #11 14 Sty 2010 08:16
    Freddie Chopin
    Specjalista - Mikrokontrolery
  • Pomocny post
    #12 14 Sty 2010 09:00
    michalko12
    Specjalista - Mikrokontrolery

    mirekk36 napisał:
    nieulotna to volatile ...

    Jesteś pewien, że "nieulotna"?

    0
  • Pomocny post
    #13 14 Sty 2010 09:23
    94075
    Użytkownik usunął konto  
  • Pomocny post
    #14 14 Sty 2010 09:43
    mirekk36
    Poziom 42  

    michalko12 napisał:
    mirekk36 napisał:
    nieulotna to volatile ...

    Jesteś pewien, że "nieulotna"?


    oczywiście, że ulotna - mój czeski błąd - słuszna twoja uwaga

    Dodano po 10 [minuty]:

    albertb napisał:

    volatile - obojętnie gdzie to tylko wskazówka dla kompilatora, aby nie optymalizować


    no pewnie, że masz rację że można dać obojętnie gdzie słówko volatile jeśli chodzi o zmienną - tylko nie mów, że wszędzie je dajesz? ;) to że można je dać wszędzie nie znaczy że wszędzie potrzeba - nieraz a w zasadzie w większości przypadków to będzie raczej przeszkadzać

    albertb napisał:

    static ma 2 różne znaczenia w zależności gdzie jest użyte:
    1. w funkcji - powoduje, że zmienna jest jak pisze atom1477 "nieulotna"
    czyli jej wartość jest zachowywana pomiędzy kolejnymi wywołaniami. Dlatego, że pamięć dla niej jest przydzielana "statycznie", a nie na stosie. Ale zmienna dostępna jest tylko w tej funkcji.

    no dokładnie tak tłumaczyłem

    albertb napisał:

    2. poza funkcją - powoduje że zmienna przestaje być widoczna dla innych plików źródłowych, przestaje być globalna, i nie pomogą wtedy żadne "dodatkowe manewry" - czyli dokładnie odwrotnie niż pisze mirekk36.


    a to kolega gdzie wyczytał w moich postach???? ;) ja pisałem o zmiennych globalnych, że trzeba coś dodatkowego zrobić żeby były widoczne w innych plikach chodziło mi o "extern" i nie miało to nic wspólnego ze zmiennymi statycznymi


    albertb napisał:

    Podsumowując jeśli autor chce mieć dane które są dostępne dla kilku funkcji, a nie przeszkadzają reszcie programu powinien funkcje dostępu do tych danych zgrupować w 1 pliku a zmienne zadeklarować z "static"


    a tutaj to ja dopytam o co chodzi bo może ja czegoś nie wiem a chętnie się dowiem. Bo jaka różnica będzie takich zmiennych zdeklarowanych w oddzielnym pliku *.c od zmiennych globalnych ??? wg mnie żadna bo tak samo będą dostępne dla każdej funkcji w każdym module każdego programu. Przecież w pliku nagłówkowym *.h dla takiego pliku *.c będzie trzeba powtórzyć deklaracje tych zmiennych tyle że właśnie chyba ze słówkiem extern - a później inkludować tylko ten plik *.h Tzn tak mi się wydaje - czy źle myślę??? jeśli źle ? - to co ty miałeś na myśli tak dokładnie

    0
  • Pomocny post
    #15 14 Sty 2010 09:48
    yogi009
    Poziom 42  

    No i zrobiła się dyskusja polityczna, a kolega tylko pytał jakiego typu zmiennej użyć :-) I otrzymał odpowiedź.

    0
  • Pomocny post
    #16 14 Sty 2010 10:26
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Jeśli zmienna "globalna" w danym pliku jest statyczna, to nie jest ona "exportowana" poza moduł (czyli plik) - żadne extern w tym momencie nie pomoże i będzie skwitowane błędem kompilacji. Dzięki takim niby nic nie znaczącym zabiegom można pokazać komuś (np sobie w przyszłości), że taka zmienna nie jest przeznaczona do użytku na zewnątrz modułu (odpowiednik private / protected w C++), dzięki czemu życie staje się łatwiejsze.

    W teorii też (podobnie jak ze statycznymi funkcjami, które są inline'owane jeśli to możliwe i zasadne), kompilator może takie zmienne zoptymalizować (zamiast odwołań do adresu korzystać z wkompilowanych wartości stałych, rejestrów itp (jeśli może oczywiście).

    4\/3!!

    0
  • Pomocny post
    #17 14 Sty 2010 10:37
    94075
    Użytkownik usunął konto  
  • #18 14 Sty 2010 11:18
    mirekk36
    Poziom 42  

    albertb --> no tak teraz rozumiem tę nieścisłość i dzięki za podpowiedzi także dla Freddie Chopin'a ;)

    też uważam, że to nie żadna dyskusja polityczna bo przy takich okazjach można dużo się dowiedzieć dyskutując

    0
  • #19 14 Sty 2010 11:42
    atom1477
    Poziom 43  

    Freddie Chopin napisał:
    Tyle że rozszerzenie .cc jest zarezerwowane dla C++

    4\/3!!


    No to tego nie wiedziałem. Zmieniam na ccc :D

    Powiem Wam że jak szukałem odpowiedzi w necie to znajdywałem dużo rzeczy , ale nie były to konkretne odpowiedzi ale kilometrowe wywody na temat obsługi wątków, optymalizacji i dotyczyły głownie C++ i programowania na kompa.
    Skoro tak się wkręciliście w dyskusje to zapytam do void.
    Co to oznacza? Doczytałem że coś niezdefiniowanego.
    Powiedzmy jak chcemy wskaźnikiem raz odwoływać się do uint8_t a raz do uint32_t to wskaźnik robimy na void i wtedy wskaźnik jest uniwersalny.
    Ale mi chodzi o coś innego.
    Mam na przykład takie coś:
    Cytat:
    static void I2C_Stop(void);

    Po co jest:
    Cytat:
    static void I2C_Stop(void);

    Skoro funkcja i tak nie przyjmuje parametrów? Czyżby po to żeby próba podana parametru nie spowodowała błędu?
    Ale brak tego powoduje warninga „function declatation isn't prototype”, czyli chyba nie bo zgodnie z moim myśleniem nie powinno być żadnego warninga.
    A co daje pierwszy void? Niezdefiniowany parametr wyjściowy? Taki że kompilator będzie go dobierał w zależności od tego gdzie chce wpisać wynik działania funkcji (mimo że funkcja nie zwraca wyniku)?
    Jak to usunę to mam warninga: „type defaukts to ‘int’ in declaration of I2C_Stop”.
    I na koniec „static”. Brak tego nie powoduje żadnego warninga. Ale ktoś mnie kiedyś gonił żebym dodał tego statica, tylko nie powiedział po co.

    0
  • Pomocny post
    #20 14 Sty 2010 11:55
    Freddie Chopin
    Specjalista - Mikrokontrolery

    void - podobnie jak inne elementy - ma różne znaczenie zależnie od miejsca.

    void function(void) to funkcja która nie przyjmuje parametrów i nic nie zwraca.

    void* to wskaźnik który może wskazywać "coś"

    Do funkcji których nie potrzebujesz w innych modułach (czyli tak jakby "wewnętrznych" dla danego pliku) dodajesz static po to, żeby za 3 miesiące wiedzieć co miałeś na myśli i po to, żeby kompilator mógł je włączyć do ciała funkcji wywołującej, jeśli uzna to za zasadne z punktu widzenia optymalizacji. Funkcja która nie jest static MUSI być osobnym tworem, ze swoim adresem w pamięci, rezerwacją stosu, zapisaniem rejestrów itd. Funkcja static już taka być nie musi = może zostać zoptymalizowana.

    Ogólnie to widzę, że chyba brniesz z C totalnie po ciemku. Może jednak naprawdę przydałby się jakiś kurs podstaw, bo niedługo już nikt nie będzie wiedział "co jest grane".

    4\/3!!

    0
  • #21 14 Sty 2010 12:18
    atom1477
    Poziom 43  

    Mam kurs C z EdW. Po prostu powili wchodzi.
    Dzieki za pomoc.

    To jeszcze ostatnie małe pytanie i zamykam temat żeby nie zrobił się taki tasiemiec nie na temat jak zwykle:
    Czy taka zmienna:

    Code:

    static int16_t IIR_1a(int16_t In)
    {
    static int16_t IIR_Coeff_X0 = 0;


    }


    Zostanie ustawiona na 0 tylko raz czy przy każdym wywołaniu funkcji?
    Bo zmienne nie static są ustawiane co raz. W końcu są na stosie i muszą być tak inicjalizowane. A tutaj to nie wiem.

    0
  • #22 14 Sty 2010 12:35
    mirekk36
    Poziom 42  

    np potrzebujesz zmienną którą będziesz zarówno aktualizował w przerwaniu i w pętli głównej programu. Gdy nie dasz jej volatile - to optymalizacja doprowadzi do tego, że w pętli głównej wrzuci sobie zawartość tej zmiennej np do jakiegoś wolnego rejestru procesora i w ramach tej pętli głównej będzie się posługiwać tylko tym rejestrem bez sprawdzania czy cośkolwiek się zmieniło w komórce RAM gdzie zmienna sobie leży. Tymczasem procedura przerwania - może zrobić podobnie tzn wrzuci do rejestru - porobi na niej swoje aktualizacje a na końcu wrzuci zawartość rejestru do tej komórki RAM gdzie leży zmienna. Ale ..... no ale pętla główna się nie zakończyła - więc nawet nie ma świadomości, że to co trzyma w rejestrze nie odpowiada wartości, która została zaktualizowana w przerwaniu. Tak samo jak przerwanie wczytując ją do rejestru nie będzie miało świadomości, że pętla główna sobie niby aktualizuje tę zmienną.

    Natomiast jak walniesz volatile ;) - to nakazujesz kompilatorowi aby obojętnie w jakiej części programu zawsze zaglądał do tej komórki i to ją aktualizował i na niej bezpośrednio wykonywał działania. Dzięki temu pętla główna za każdym razem gdy zwiększy zawartość tej zmiennej to już nie w jakimś rejestrze a bezposrednio w tej komórce. Oczywiście jak się spojrzy na kod w asemblerze to będzie taka peracja zajmować kilka rozkazów więcej a co za tym idzie kilka cykli zegarowych więcej. No ale coś za coś - i tak fajnie, że C daje wybór w taki sposób ;)

    Dodano po 1 [minuty]:

    atom1477 napisał:

    Zostanie ustawiona na 0 tylko raz czy przy każdym wywołaniu funkcji?
    Bo zmienne nie static są ustawiane co raz. W końcu są na stosie i muszą być tak inicjalizowane. A tutaj to nie wiem.


    taka zmienna zostanie tylko raz ustawiona i to przy starcie programu a nie przy wchodzeniu do funkcji

    a już było pisane że zmienne static właśnie nie są na stosie ;) i dlatego jest taka możliwość, że inicjalizowane są przed uruchomieniem funkcji main - tylko raz

    0
  • Pomocny post
    #23 14 Sty 2010 12:40
    94075
    Użytkownik usunął konto  
  • Pomocny post
    #24 14 Sty 2010 12:40
    Freddie Chopin
    Specjalista - Mikrokontrolery

    static int16_t IIR_Coeff_X0 = 0;

    Nie ma potrzeby inicjalizowania zmiennych globalnych i statycznych wartością zero, bo zmienne takie tak czy siak są inicjalizowane na zero (znajdą się w sekcji .bss która jest zerowana w startupie).

    4\/3!!

    0
  • #25 14 Sty 2010 13:01
    atom1477
    Poziom 43  

    Tak, ale nie zawsze jest mi potrzebne zero. Wiele ma być o wartości 1.
    Ale rozumiem że skoro są normalnie w RAMie to zainicjalizuje je zwykły inicjalizator, jeden raz.
    No to wielkie dzięki wszystkim.
    Zamykamy.

    0
  • Pomocny post
    #26 14 Sty 2010 14:14
    yogi009
    Poziom 42  

    A czasem takie niezdefiniowane mogą przyjmować wartość nieokreśloną NIL, nie wiem jak w C... dlatego dobra szkoła jednak każe przypisywać te zera.

    0
  • #27 14 Sty 2010 14:19
    Freddie Chopin
    Specjalista - Mikrokontrolery

    yogi009 napisał:
    A czasem takie niezdefiniowane mogą przyjmować wartość nieokreśloną NIL, nie wiem jak w C... dlatego dobra szkoła jednak każe przypisywać te zera.

    Nie mogą jeśli są to zmienne statyczne albo globalne. Standard wymaga, aby były zainicjalizowane wartością 0 i zawsze tak jest, chyba że wiesz jak to pominąć. Pięć zabezpieczeń nie jest też żadną dobrą szkołą - w ten sposób można wiele rzeczy zrobić za kompilator (bo może o czymś "zapomnieć", a nawet jeśli nie, to przecież nie zaszkodzi), ale to jest tylko źródło potencjalnych problemów i spowolnienia / zwiększenia programu.

    4\/3!!

    0
  • Pomocny post
    #28 14 Sty 2010 15:48
    tmf
    Moderator Mikrokontrolery Projektowanie

    Yagi009 - tak jak pisze FCh zmienne static sa inicjowane implicite na 0, chyba, ze to zmienisz definiujac je inaczej. Nie ma wiec sensu ich inicjowac, jesli poczatkowa wartoscia ma byc 0 - to nie tylko jest bez sensu, ale proewadzi do wydluzenia kodu - czasami optymalizator da ciala i twoja ponowna inicjalizacja nie zostanie wyrzucona. W swiecie embedded, w ktorym sie poruszamy taka nieoszczednosc nie jest dobrym pomyslem. Wskazniki tez sa domyslnie ustawiane na 0, czyli NULL.

    Dodano po 7 [minuty]:

    atom1477 napisał:
    .
    Skoro tak się wkręciliście w dyskusje to zapytam do void.
    Co to oznacza? Doczytałem że coś niezdefiniowanego.
    Powiedzmy jak chcemy wskaźnikiem raz odwoływać się do uint8_t a raz do uint32_t to wskaźnik robimy na void i wtedy wskaźnik jest uniwersalny.


    Nie, void to po prostu taki sam typ jak kazdy inny (no prawie taki sam). Robienie wskaznika void* po to, zeby wskazywal raz na uint8_t a raz na uint32_t jest powaznym bledem. Przede wszystkim rezygnujesz wtedy z kontroli typow oferowanej przez kompilator - czyli narazasz sie na wiele nieswiadomych bledow, przed ktorymi normalnie dostawalbys ostrzezenia. Druga rzecz to, ze dla takiego wskaznika sa niezdefiniowane operacje ++ i --, generalnie arytmetyka na void nie dziala (bo nie ma jak). Teraz sobie wyobraz sytuacje, ze masz wskaznik typu void, wskazujacy na uint8_t, a ty pomylkowo w programie go traktujesz jako uint32_t - robi sie masakra. Do tego ewentualnie sluzy rzutowanie typow, tez zlo, ale mniejsze. Czyli wskaznik typu uint32_t *ptr, mozesz traktowac jako uint8_t po zastosowaniu rzutowania (uint8_t*)ptr.

    0
  • #29 14 Sty 2010 15:57
    yogi009
    Poziom 42  

    Ok, ja się zgadzam z tym, szczególnie że otwarcie przyznaję, że akurat C nie znam od strony praktycznej, natomiast patrząc dydaktycznie, z punktu widzenia większej ilości języków programowania, podejście które przedstawiłem wcale nie jest takie pozbawione sensu. Myślę, że już chyba wszystko w tym temacie sobie wyjaśniliśmy, serdecznie pozdrawiam Szanownych Kolegów, zawsze miło podyskutować w towarzystwie posiadających głęboką wiedzę.

    0
  • #30 14 Sty 2010 16:19
    atom1477
    Poziom 43  

    Oczywiście wiem że wskaźnik void nie będzie zbyt mądrym rozwiązaniem i ja go nie stosuję.
    Np. kompilator nie będzie wiedział jak go zwiększyć o jeden bo nie będzie wiedział na jaki typ wskazuje wskaźnik.
    Ale takie coś widziałem i dlatego tak napisałem.

    0