loading...
0 3

Bliski kuzyn wskaźnika

Bliski kuzyn wskaźnika

W poprzednim artykule (Wskaźniki – najbardziej uniwersalne narzędzie programisty) opisałem korzyści i zagrożenia wynikające z zastosowania wskaźników. W e!COCKPIT zaimplementowano także obsługę referencji, która umożliwia korzystanie z wielu zalet wskaźników, eliminując jednocześnie związane z nimi problemy.

PROGRAM PLC_PRG

VAR
refZmienna: REFERENCE TO INT;
iZmienna: INT;
END_VAR

(*kod programu*)

iZmienna:=123;
refZmienna REF=iZmienna;

Powyższy przykład pokazuje tylko ogólną zasadę działania. W tym przypadku, aby uzyskać wartość refZmienna, nie trzeba korzystać z operatora ^ jak to miało miejsce we wskaźnikach.

Różnice pomiędzy referencją a wskaźnikiem

Zasada działania referencji jest podobna do wskaźników. Dzięki nim możliwe jest odwoływanie się do zmiennej bez konieczności kopiowania jej. W powyższym przykładzie zarówno zmienna iZmienna, jak i refZmienna mają taką samą wartość. Co ważne, zmiana wartości którejkolwiek z nich będzie mieć wpływ na drugą. Mamy tu więc do czynienia z podobnym połączeniem jak w przypadku zmiennych VAR_IN_OUT w blokach funkcyjnych.

W przeciwieństwie do wskaźników, kompilator chroni nas przed popełnieniem podstawowych błędów. Na przykład nie jest możliwe skompilowanie programu, jeśli chcemy połączyć referencję ze zmienną nieodpowiedniego typu. Kompilator zwróciłby więc błąd, jeśli próbowalibyśmy wykonać następujący kod:

PROGRAM PLC_PRG

VAR
refZmienna: REFERENCE TO INT;
wZmienna: WORD;
END_VAR

(*kod programu*)

wZmienna:=123;
refZmienna REF=wZmienna;

 

 

Wiąże się to jednak z pewnymi ograniczeniami. Referencji nie można wykorzystywać do konwersji typów zmiennych. Nie jest też możliwe przetwarzanie danych w tablicach o dowolnych rozmiarach.

Zastosowanie referencji

Ze względu na powyższe właściwości zastosowanie ogranicza się przede wszystkim do bezpośredniego odwoływania się do zmiennych. Dalej dzięki temu możliwe jest uzyskiwanie funkcjonalności zmiennych VAR_IN_OUT nie tylko w blokach funkcyjnych, ale także w funkcjach.

W przeciwieństwie do wskaźników nie ma za to ryzyka odwoływania się do nieistniejących obszarów pamięci. Dla potencjalnego użytkownika takiej aplikacji dużą zaletą jest łatwiejsze odwoływanie się do wartości referencji oraz intuicyjne podłączanie zmiennych pod bloki funkcyjne, w których wykorzystano referencje.

FUNCTION FunMnozenie2 :BOOL

VAR_INPUT
refZmiennaWejsciowa:REFERENCE TO INT;
END_VAR
VAR
iTemp:INT;
END_VAR

(*kod funkcji*)

iTemp:=refZmiennaWejsciowa;
refZmiennaWejsciowa:=iTemp*2;

FunMnozenie2:=true;

 

PROGRAM PLC_PRG

VAR
iZmienna:INT:=1;
END_VAR

(*kod programu*)

FunMnozenie2(iZmienna);

Jak widać, podłączenie zmiennej pod wejście zadeklarowane jako REFERENCE TO INT dla użytkownika nie różni się niczym od podłączenia do wejścia zadeklarowanego jako INT.

Kolejną zaletą jest możliwość inicjalizacji referencji w oknie deklaracji zmiennych. Dzięki temu połącznie pomiędzy referencją a zmienną źródłową wykonywane jest tylko raz, a  nie ponawiane w każdym cyklu sterownika, co może wpływać na wydajność programu.

PROGRAM PLC_PRG

VAR
refZmienna: REFERENCE TO INT:=iZmienna;
iZmienna: INT;
END_VAR

(*kod programu*)

(*niepotrzebny fragment kodu : refZmienna REF=iZmienna; *)
iZmienna:=123;

Dodatkowo referencje umożliwiają tworzenie uniwersalnych bloków funkcyjnych dzięki zastosowaniu funkcji „__ISVALIDREF()”, która określa, czy pod referencję prawidłowo została podpięta zmienna.

FUNCTION_BLOCK FbSterowanieOswietleniem
VAR_INPUT
xPrzelacznik : BOOL;
refSwiatlo1 : REFERENCE TO BOOL;
refSwiatlo2 : REFERENCE TO BOOL;
END_VAR(*kod programu*)
IF xPrzelacznik THEN
IF __ISVALIDREF(refSwiatlo1) THEN
refSwiatlo1 := TRUE;
END_IF
IF __ISVALIDREF(refSwiatlo2) THEN
refSwiatlo2 := TRUE;
END_IFELSE
IF __ISVALIDREF(refSwiatlo1) THEN
refSwiatlo1 := FALSE;
END_IF
IF __ISVALIDREF(refSwiatlo2) THEN
refSwiatlo2 := FALSE;
END_IF
END_IF

W ten sposób możemy na przykład sterować oświetleniem podłączając pod wejścia bloku funkcyjnego dostępne w pomieszczeniu obwody świetlne.

Częste błędy

Wskaźnik jest po prostu zmienną typu DWORD. W związku z tym nieprzypisanie mu żadnej wartości sprawi, że zostanie zainicjowany z wartością 0. W programie warto sprawdzać, czy nie występuje taka sytuacja, ponieważ kompilator nie będzie zgłaszał żadnych błędów.

W przypadku referencji programista jest zobowiązany do przypisania referencji do zmiennej źródłowej. Często może okazać się, że przypisanie wykonywane jest dynamicznie, w trakcie działania aplikacji. W takim przypadku należy zadeklarować „pustą” zmienną, która na etapie kompilacji zostanie połączona z referencją. Poniżej przykład takiego rozwiązania.

PROGRAM PLC_PRG

VAR
refZmienna: REFERENCE TO INT:=iDummy;
iDummy:INT;
iZmienna: INT;
xPrzypisz:BOOL;
END_VAR

(*kod programu*)

iZmienna:=123;

IF xPrzypisz THEN
refZmienna REF=iZmienna;

END_IF

Dopiero kiedy zmienna xPrzypisz zostanie ustawiona na TRUE, referencja zacznie działać prawidłowo.

VAR_IN_OUT – czyli referencja i wskaźnik, który znamy i lubimy

Tak naprawdę każdy programista ma na co dzień styczność z referencjami, nawet jeśli nie jest tego świadomy. Zmienne VAR_IN_OUT działają bowiem dokładnie tak samo jak opisane tu referencje. Jeśli w bloku funkcyjnym korzystamy ze zmiennej VAR_IN_OUT, to w przeciwieństwie do zmiennych VAR_IN i VAR_OUT nie jest tworzona wewnętrzna kopia podłączonej zmiennej, a tylko przekazana referencja do zmiennej źródłowej.

Podsumowanie

Mam nadzieję, że ten i poprzedni artykuł oswoiły trochę zagadnienia wskaźników i referencji. Jednocześnie zachęcam do samodzielnego eksperymentowania i dalszej nauki. Jest to podstawa przed poznaniem bardziej zaawansowanych technik programistycznych.

Krzysztof Nosal, WAGO.PL

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *