Dlaczego embedded Strictly C / C ++ [zamknięty]


15

Nie podobało mi się to pytanie, ponieważ nie można na nie łatwo odpowiedzieć, ale być może mogę sformułować: „Co powstrzymuje Embedded przed zmianą języków?”

Na przykład prawie widzimy C / C ++ dla osadzonych (myślę, że słyszałem też o ADA wspomniane wcześniej? Popraw mnie, jeśli się mylę)

Ale co dokładnie powstrzymuje świat Embedded przed zmianą języków? Czy to po prostu, że C jest zbyt łatwy w użyciu, czy po prostu nie ma „potrzeby” zmiany, ponieważ C robi wszystko dobrze?

To zawsze mnie zaskoczyło, nie narzekam. Ponieważ trzymanie go w kilku językach zapewnia standaryzację. Ale wciąż pozostaje pytanie.

Zdaję sobie sprawę, że jest to pytanie subiektywne, jednak moim głównym pytaniem jest „dlaczego”, a nie „IF / WHEN”


2
Czy jest jakiś konkretny język wyższego poziomu, który chciałbyś widzieć w systemach wbudowanych? EDYCJA: a raczej, jakie funkcje językowe są zainteresowane tym, że C nie zapewnia?
Jon L

1
@JonL - Jest kilka funkcji niskiego poziomu, które chciałbym mieć C. EG lepsza manipulacja bit / nibble / byte / word wewnątrz dużych int. Lepsze wsparcie bezpieczeństwa, EG ma funkcje, które ma Ada.
Rocketmagnet

3
Osadzony jest nie ściśle C. Oto grono języków wysokiego poziomu dla systemów wbudowanych: electronics.stackexchange.com/questions/3423/...
Kellenjb

1
„Osadzony” ma różne znaczenia. 4-bitowy mikrokontroler uruchamiający toster różni się od ECU lub przystawki STB. To spektrum sprawia, że ​​na twoje pytanie trudno odpowiedzieć.
Toby Jaffey,

1
I zgodnie z oczekiwaniami zostało to zamknięte. To pytanie, które miałem nadzieję, otrzyma wysokiej jakości odpowiedzi, a ludzie będą pracować, aby zachować je jako świetne pytanie, tak nie jest. Zamiast tego otrzymujemy wiele odpowiedzi, które są jednym zdaniem, które otrzymują wiele pozytywnych opinii, mamy odpowiedź, która jest prozą, która ma wojnę z oceną z mnóstwem flag, a inne odpowiedzi wygenerowały więcej flag w ciągu 1 dnia, a następnie reszta strony połączona . Problem polega na tym, że dla wielu osób istnieje wiele różnych właściwych odpowiedzi na pytanie, dlaczego się nie zmieniły.
Kortuk

Odpowiedzi:


18

Po pierwsze: zapomnij o „osadzeniu”, ponieważ nie jest to użyteczne rozróżnienie. Najważniejszą właściwością jest „ograniczenie zasobów”. Najważniejszym zasobem jest często czas, w którym to przypadku mówimy o systemach czasu rzeczywistego, ale może to być również pamięć lub moc.

  • Przyjęcie nowego języka jest trudne i rzadkie. Wymaga ponownego szkolenia, nowych narzędzi i znalezienia dobrego sposobu pracy z nowym językiem. Jest to kosztowne, szczególnie dla pierwszych użytkowników. Jest to również problem z kurczakiem i jajami: bez dużej bazy użytkowników nie byłoby narzędzi i bibliotek wysokiej jakości, ale bez nich nie byłoby dużej bazy użytkowników. Dlatego nowy język musi mieć dużą przewagę nad istniejącymi, w przeciwnym razie nie będzie miał szans.

  • Większość „najnowszych” zmian w językach wypełnia lukę między dostępną mocą procesora a tym, czego potrzebuje użytkownik. Innymi słowy: mogą być nieefektywne pod względem prędkości, ale kompensują to, ponieważ są łatwiejsze dla programisty. Pomyśl o powstaniu języków takich jak Java, Python, Perl, Tcl, które są zasadniczo uruchamiane przez interpretera (być może po kompilacji) i intensywnie wykorzystują dynamiczne zarządzanie pamięcią. Ale to nie pasuje dobrze do świata o ograniczonych zasobach, w którym chcemy uzyskać a) jak najwięcej z dostępnych zasobów, nawet kosztem większego wysiłku programistycznego, i b) przewidywalne wykorzystanie zasobów.

  • C i C ++ (lub odpowiedni podzbiór) są nadal powszechnie używanymi językami najwyższego poziomu (wystarczająco, aby dostępne były dobre narzędzia, wystarczająco przeszkoleni programiści i obszerne biblioteki), które mogą spełnić przewidywalne wymagania dotyczące miejsca i czasu, które nie są zbyt daleko z tego, co jest możliwe na obecnym sprzęcie. Myślę, że jedynym rywalem jest Ada, ale miał zły start: pierwsze implementacje były (postrzegane jako?) Zbyt wolne i nieefektywne, a teraz (mimo że dostępne są dobre implementacje) język jest nieco opóźniony cechy (w porównaniu do C ++). Osobiście uważam, że szkoda, bo inne rzeczy są sobie równe Wolę latać samolotem zaprogramowanym w Adzie niż samolotem wykonanym w C lub C ++.


+1 - fajna odpowiedź. Ada wydaje się interesującym językiem, czy są jakieś kompilatory Ada dla małych mikrometrów?
Oli Glaser,

Istnieje GNAT, kompilator GCC Ada. Ale AFAIK nie był używany zbyt często na mikrach, więc trudno będzie ci znaleźć coś, co jest gotowe do uruchomienia.
Wouter van Ooijen

Tak, widziałem GNAT wspomniany na stronie Wiki. Masz rację, niewiele w okolicy dla małych mikr, ale wydaje się, że istnieje spore wsparcie (jak można się spodziewać) dla krytycznych zadań na 68k, x86, MIPS itp. (Np. DDCI )
Oli Glaser

Widzę, że jest też SPARK Ady, jak wspomniała Deek poniżej. Będę musiał to sprawdzić, gdy będę miał trochę tych nieuchwytnych rzeczy, które nazywają czasem ...
Oli Glaser,

2
Ada w postaci Gnata działa dobrze na mikroprocesorze AVR, jak widać w Arduino. Najmniejszy plik wykonywalny Gnata, jaki zbudowałem, miał 65 bajtów. Wprawdzie wszystko, co zrobił, to mrugnięcie diodą LED, chociaż równoważny szkic Arduino miał ponad 1K. Kiedy mój plik wykonywalny osiągnął 600 bajtów, sterował niezależnie 2 silnikami krokowymi ... Nie potrzebujesz SPARK - chyba że chcesz udowodnić, że Twój migacz LED jest formalnie poprawny!
Brian Drummond,

9

W przypadku systemów wbudowanych opartych na 8 i 16-bitowych mikrokontrolerach sprowadza się to do łatwiejszego tworzenia oprogramowania, które może zmieścić się w ograniczonych zasobach tych bardzo skromnych ograniczeń pamięci (być może kilka 100 bajtów pamięci RAM dla 8-bitowych mikrokontrolerów z niższej półki) , z 2-8 KiB ROM lub EPROM / Flash do przechowywania kodu).

W takich przypadkach najczęściej używanymi językami programowania są małe języki, takie jak C lub asembler. Jako bardzo przybliżone porównanie względne, kompletny asembler i kompilator C99 mogą zmieścić się na pojedynczej dyskietce, podczas gdy potrzebujesz kilku MiB dla nowoczesnego systemu programistycznego C ++ (z STL itp.).

Jeśli patrzysz na zaawansowane mikrosfery (wysokiej klasy 16-bitowe, a głównie 32-bitowe, z dość rzadkimi 64-bitowymi) i DSP w środowiskach osadzonych, ograniczenia słabną, a rozwój oprogramowania może stanowić większość rozwoju wysiłku, dlatego warto korzystać z najbardziej produktywnych narzędzi programistycznych, w tym bardziej zaawansowanych języków z funkcjami, takimi jak języki programowania obiektowego (OOP), takie jak C ++ i nowsze języki (Java, Perl, Ruby, Python).

W asemblerze i C można przewidzieć, ile pamięci jest używane, dzięki czemu możliwe jest projektowanie z ograniczeniami przestrzennymi, ale zaawansowane funkcje, takie jak szablony, obsługa wyjątków i wiązanie w czasie wykonywania uniemożliwiają dokładne poznanie niezbędnego zajętości pamięci dla standardowego programu C ++ z góry. Nie wiem wystarczająco dużo o MISRA C ++ , która jest podzbiorem C ++, aby komentować.

Języki oparte na maszynach wirtualnych z uruchomionym kodem bajtowym (Java, Perl, Python) są mniej dojrzałe w doświadczeniu wbudowanego programisty, a ponieważ języki te zostały zaprojektowane tak, aby izolować programistę od konkretnego sprzętu, utrudnia to również sumienie takie ograniczenia i ograniczenia wbudowanego systemu sprzętowego. Jest to mniejszy problem w przypadku szybkich 32-bitowych procesorów (np. ARMv7) z MiB, jeśli nie GiB RAM.

Wszystkie implementacje BASIC, o których wiem, są dość uproszczone pod względem funkcji językowych, pozostając w dużej mierze zgodne ze spuścizną Dartmouth BASIC z lat 60. Oznacza to, że język nie ma żadnych skomplikowanych bibliotek wykonawczych ani obsługi wyjątków, a interpreter lub kompilator jest dość prosty do napisania i ma również niewielki rozmiar pliku. Większość mikrokontrolerów ma do dyspozycji co najmniej jeden kompilator BASIC.

Mam nadzieję, że ogólne objaśnienia przedstawiają powody, dla których C i asembler są używane głównie w mniejszych lub starszych systemach wbudowanych, a przy ograniczeniach nowszych średnio-zaawansowanych systemów wbudowanych różnią się tylko nieznacznie od tradycyjnego komputera stacjonarnego.


5

W większości odpowiedzi podano już przyczyny historyczne (dobrze znane, wszyscy go używają, zmiana nawyków itp. Nie byłaby łatwa). Chociaż się z nimi zgadzam, powinniśmy pamiętać, że jest jeszcze jeden ważny powód.

To nie jest tak, że „C to zły lub przestarzały wybór, ale nadal używamy go z przyzwyczajenia” (jak klawiatury QWERTY).

C jest sam w sobie bardzo dobrym wyborem do programowania wbudowanego, szczególnie w aplikacjach o krytycznym czasie. Dlaczego?

  • jest na tyle niski, że można go łatwo wykorzystać do wdrażania programów w czasie rzeczywistym. Jeśli potrzebujesz zmierzyć czas w nanosekundach, musisz złapać przerwanie co 5 mikrosekund lub musisz użyć dokładnie 64 bajtów całkowitej pamięci RAM, wówczas przy bardzo wysokim poziomie języka najczęściej byłoby to niemożliwe lub bardzo trudne do rozwiązania . C daje znacznie lepszą kontrolę nad sprzętem niż języki wyższego poziomu, jest to jedna z najważniejszych różnic między programowaniem na komputery wbudowane a PC.

  • jest wystarczająco wysoki, aby można go było szybko i łatwo kodować w porównaniu z asemblerem.

Zatem C jest najlepszym (lub jednym z najlepszych) kompromisów między szybkością i bezpośrednim dostępem do sprzętu asemblera, a łatwym czytaniem i zrozumieniem języków wysokiego poziomu.


1
Myślę, że głównym aspektem na korzyść C jest to, że pozwala on zoptymalizować kod dla konkretnej platformy, jednocześnie umożliwiając działanie takiego kodu (być może nie tak wydajnie) na innych. Na czymś takim jak PIC, wiele instrukcji C przełoży się przewidywalnie na instrukcje maszynowe; taka pętla unsigned char i=63,j=128; do {something;} while(--j); while(--i);nie będzie tak czytelna jak unsigned int i=16000; do {something;} while(--i);, ale będzie działać szybciej i będzie bardziej wydajna na PIC. Gdyby kod został przeniesiony do ARM, drugie podejście byłoby bardziej wydajne, ale pierwsze nadal działałoby.
supercat

4

Jest to dokładnie ten sam powód, dla którego w zwykłym programowaniu (najczęściej używane) języki się nie zmieniają (naprawdę):

  1. Ogromna ilość istniejącego kodu (biblioteki / istniejące implementacje)
  2. Duży zestaw narzędzi, który może współpracować z tymi językami (IDE, symulatory, ...)

4

W świecie osadzonym dostarczanie aktualizacji oprogramowania może być o wiele trudniejsze (lub niemożliwe), dlatego tym bardziej ważne jest zagwarantowanie poprawności. Niestety C zapewnia bardzo niewielką pomoc w tym zakresie i pozwala programiście grać szybko i swobodnie.

Boli mnie używanie C w systemach wbudowanych i chciałbym przynajmniej przejść do C ++ ze względu na wiele korzyści, jakie zapewnia w postaci ograniczeń, takich jak const, referencje, pisanie stringer itp.

Myślę, że odpowiedź brzmi: po prostu utknęliśmy w C, ponieważ zmiana nie jest opłacalna z handlowego punktu widzenia. Wszyscy znają C, jest tam mnóstwo kompilatorów, bibliotek i narzędzi do jego generowania. W nowym języku zaczynamy od zera.

Chyba dlatego ludzie nadal używają PHP .

Podwójny młotek PHP.


Jeśli chcesz przedyskutować pytanie, użyj komentarzy lub meta, jeśli chcesz poklepać użytkownika po plecach, aby uzyskać dobre pytanie, oceń je lub skomentuj.
Kortuk

Zawsze możesz użyć Pascala - wydaje się, że ma dodatkowe ograniczenia, których szukasz :-). Lub jakaś forma Super-Lint.
Russell McMahon,

2
Jednym z prawdopodobnie bardzo znaczących powodów dla C jest to, że OSTATNIE łatwiej jest napisać podstawowy kompilator C niż kompilator C ++. Przez jakiś czas pracowałem nad jednym, zanim odciągnęły mnie ważniejsze zadania. Zabawne rzeczy! Pisanie kompilatora C ++? Ugh. Jednak wolę C ++ jako użytkownika.
darron

1
@ RussellMcMahon - Nie mogę używać Pascala, ponieważ nie ma kompilatora Pascala dla MCU, których używam.
Rocketmagnet

@darron - To bardzo dobra uwaga. Istnieją jednak bardzo dobre kompilatory C ++ typu open source, takie jak gpp. Musieliby po prostu napisać za to zaplecze.
Rocketmagnet

4

Czy nikt tutaj nie słyszał o SPARK Adzie?

Jest to „mała” wersja języka Ady i powiązanych narzędzi programistycznych dla systemów wbudowanych, np. Awioniki i innych aplikacji krytycznych dla bezpieczeństwa, takich jak sprzęt medyczny.

Badania wykazały tylko 5-10% spadek prędkości przetwarzania w porównaniu do C / C ++ z bardziej niezawodnym kodowaniem SPARK.

Myślę, że rozprzestrzenianie się C w systemach wbudowanych wynika z przyczyn ekonomicznych:

  • Jest już dostępny i zwykle nadaje się do większości zastosowań - a większość aplikacji pod względem objętości nie jest krytyczna, nikt nie umrze, jeśli pralka się przepełni - więc po co zmieniać?

  • Zestaw narzędzi SPARK będzie sam w sobie dodatkowym wydatkiem i przeszkoleniem personelu w zakresie korzystania z niego.

  • Dodatkowe zalety SPARK (lub innych języków innych niż C) dla wbudowanego kontrolera / systemu zarządzania mogą być niewystarczające, aby uzasadnić niezbędną premię w cenie produktu w oczach konsumenta - zwłaszcza gdy widzą, jak się wydaje, że „dobre” konkurencyjne marki sprzedają się za niższą cenę.

  • Firma AdaCore stara się nie wchodzić zbyt głęboko w aplikacje rynku masowego, ponieważ nieuchronnie będą one wymagały dużego wzrostu personelu wsparcia technicznego, aby zająć się nieistotnymi kwestiami. AdaCore jest firmą ekspercką wysokiego szczebla, jest dumna z tego i oferuje swoje produkty i usługi firmom zajmującym się zaawansowanymi technologiami. Język nie jest w stanie przeniknąć na nowe rynki, chyba że jego główni interesariusze naprawdę tego chcą.

Tak więc, @ Wouter, nie musisz się martwić śmiercią na niebie z powodu braku kodu osadzonego w Adzie!

Już od wielu lat znajduje się w systemach samolotów. Podobnie w przypadku rozrusznika serca.

Ale w przypadku zmywarki do naczyń, systemu kontroli usług budowlanych, sterownika pieca laboratoryjnego i innych nie tak ściśle regulowanych aren - czy ekonomicznie warto przejść dodatkowe kroki?


Ciekawe, dzięki - nie słyszałem o SPARK, sprawdzę to.
Oli Glaser

Niektóre badania pokazują przyspieszenie w stosunku do istniejącej aplikacji w C - spójrz na serwer DNS „Ironsides” ...
Brian Drummond,

3

Myślę, że głównym powodem popularności C jest to, że po pierwsze: C jest popularny i wiele osób go zna, a po drugie: Żaden z nowych popularnych języków, takich jak Java, C #, a nawet wiele aspektów C ++, nie nadaje się do pracy osadzonej. Zasadniczo 3 inne języki, o których wspomniałem, polegają w dużej mierze na pamięci dynamicznej, która niesie ze sobą niedeterministyczne wykonywanie programu, obiektów, które przynoszą ze sobą pamięć dynamiczną, duże wymagania pamięciowe (ponieważ jedną z ważniejszych stron OO jest użycie większa liczba klas), rosnąca popularność kompilacji „just in time” (i wiele platform wbudowanych nie może nawet w ogóle skompilować własnego kodu C) ...

Istnieje również fakt, że wiele bibliotek dostarczanych z, powiedzmy, Java lub C # jest bezużytecznych dla dużej liczby osadzonych projektów.

Z drugiej strony mamy starsze języki, takie jak Pascal lub Basic. Z mojego punktu widzenia nie są one tak popularne, ponieważ C stał się językiem „standardowym w branży” i bardzo duża liczba programistów i inżynierów uczy się dzisiaj C. W niektórych szkołach Pascal lub Basic nawet się nie uczą. Istnieje również fakt, że wiele popularnych obecnie języków ma składnię podobną do C, a używanie Pascala byłoby dziwne dla programisty C.

Jeśli chodzi o FORTRAN, myślę, że trafił w niszę i jest używany głównie przez inżynierów i naukowców pracujących w obszarach, w których istnieje odpowiedni ekosystem do jego wykorzystania. Nie widzę żadnego konkretnego powodu (oprócz tych, które wymieniłem dla Pascala i Basic), nie jest on używany w systemach wbudowanych.

Zauważ, że w tej odpowiedzi skupiłem się głównie na mniejszych systemach. Istnieje również wiele urządzeń wbudowanych, które wykorzystują bardziej złożone systemy operacyjne, takie jak GNU / Linux lub inne pochodne Uniksy, a do ich programowania można użyć mniej więcej dowolnego języka popualr.


1
C jest popularny, ponieważ C jest popularny? :-)
stevenvh,

2
@stevenvh Tak, to prawda. To rodzaj pętli pozytywnego sprzężenia zwrotnego. Im bardziej popularny, tym bardziej popularny.
AndrejaKo,

3

C jest bardzo prostym językiem i był wielokrotnie wymieniany jako fantazyjnego języka asemblera . Jest to prawie minimalna ilość abstrakcji, którą można podać powyżej kodu asemblera, ponieważ konstrukcje C odwzorowują dość bezpośrednio na konstrukcje na poziomie maszyny.

Z tego i kilku innych powodów bardzo łatwo zaimplementować kompilator C na nowym układzie. Wiele pracy zostało już wykonanych, względnie niewielka złożoność lub problemy mogą się nie udać, a kontrola niskiego poziomu pozwala dość łatwo poradzić sobie z wszelkimi dziwactwami posiadanymi przez sprzęt.

C ++ może być (pierwotnie został) zaimplementowany jako warstwa translacji kodu źródłowego na C, co oznacza, że ​​otrzymujesz C ++ (lub przynajmniej jego wersję) za darmo z kompilatorem C.

Dzięki C i C ++ jesteś bootstrapem prawie wszystkiego, czego potrzebujesz do nowego układu, co czyni go logicznym miejscem do rozpoczęcia.


3

Niektóre powody, o których inni nie wspominali:

  • Miejsce na problem: C nadaje się do małych i prostych systemów. Jeśli wszystko, co robisz, to reagujesz na sygnały zewnętrzne i wypychasz kilka cyfr, C działa raczej dobrze (bez skomplikowanych struktur danych, bez malloc, bez złożonej obsługi błędów).

  • Wielkość produkcji: jeśli masz duże przebiegi produkcyjne, ekonomicznie uzasadnione jest oszczędzanie na każdej jednostce sprzętowej i wydawanie więcej na programistów, ponieważ programowanie to jednorazowy koszt.


2

Myślę, że dzieje się tak, ponieważ C / C ++ to języki najniższego poziomu, wysokiego poziomu.


1

W rzeczywistości dla małych systemów wbudowanych C jest znacznie bardziej popularny niż C ++. Powód jest taki sam, jak w przypadku braku używania innych języków. C ++ wymaga środowiska wykonawczego, chyba że podasz większość funkcji, które odróżniają go od C.

Oprócz asemblera, C jest jedynym znanym językiem, który kompiluje się do natywnego kodu i dla którego posiadanie środowiska wykonawczego jest opcjonalne. Gwarantuje to, że będzie to najmniejszy ślad i najszybszy czas wykonania w ograniczonym środowisku (z wyjątkiem przypadku użycia zespołu).

Z drugiej strony, w średnich i dużych systemach wbudowanych (przez co mam na myśli więcej pamięci i zegara, większy rozmiar słów) nie powiedziałbym, że C (lub C ++) jest tak powszechny. Widziałem systemy obsługujące Python, Forth ... nawet Java.

Ale oczywiście prawie zawsze masz możliwość używania C / C ++, oczywiście z tych samych powodów, o których wspomniałem powyżej. A mając tę ​​opcję i będąc osobą, która już dobrze zna C dla małych komputerów, dlaczego miałbyś wybrać inny język?


4
C ++ może generować dużo narzutu, ale w pełni zgodny kompilator C ++, którego użyłem dla MSP430, nie wymagał środowiska wykonawczego, C ++ skompilował się do kodu natywnego. Przykro mi, że mówienie innym to szkoda, a ja głosowałem wam. Możesz usunąć głos negatywny, podając referencje, które przekonają mnie, że jestem niepoprawny (co będzie trudne, przeczytałem listę asemblacji skompilowanego C ++ dla moich projektów, w celu upewnienia się, że kompiluje się wydajnie) lub możesz usunąć swoją odpowiedź, która usunie wpływ na twoją reputację (chociaż w tym momencie otrzymujesz +8 powtórzeń netto)
Kortuk

3
W pełni zgadzam się z Kortukiem. Niektóre części C ++ wymagają szerokiego wsparcia w czasie wykonywania, ale ta część, która nie jest, jest nadal znacznie lepszym C (i całkowicie OO). Ograniczenia do tego podzbioru są łatwo egzekwowane przez niektóre przełączniki kompilatora i linkera. W niektórych częściach (na przykład przerażający printf) C ++ ma co najmniej potencjał językowy, który wymaga znacznie mniej wsparcia środowiska wykonawczego (gdyby tylko std :: cout zostały zaimplementowane z myślą o małych systemach ...)
Wouter van Ooijen

1
@Kortuk, przepraszam, że nie było jasne, ale kiedy powiedziałem, że „C jest jedynym językiem, który ...” nie znaczy, że C ++ nie ma obu tych rzeczy, miałem na myśli, że C ma kombinację tych dwóch a C ++ ma jeden z nich. Mój nacisk położono na część wykonawczą. Nie twierdzę też, że używanie C ++ bez środowiska wykonawczego jest całkowicie niemożliwe, ale jest dość niezwykłe. Nie widzę, jak możesz na przykład obsługiwać wyjątki i RTTI bez środowiska wykonawczego, a są to dość ważne funkcje. Ale przepraszam, że sposób, w jaki to wyraziłem, doprowadził do możliwych nieporozumień.
fceconel,

@ fceconel, nigdy nie korzystałem z C ++ w środowisku wykonawczym i dyskutujemy tutaj o systemach osadzonych, nigdy nie korzystałem z czasu wykonywania na moich mikrokontrolerach. To pytanie jest nieco inne niż być może przeczytałeś, pyta, dlaczego C / C ++ są jedynymi powszechnymi wyborami, a nie dlaczego C zamiast C ++. Przyznaję, że używając czegoś tak prostego, jak cout nigdy nie zdarzy się w mojej mikro, mam kilka wolnych pinów, a nie ekran.
Kortuk
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.