GUT Intel 2015/16 1/40 Inżynieria Wytwarzania Systemów Wbudowanych Wykład 1 Iwona Kochańska Katedra Systemów Elektroniki Morskiej WETI PG October 5, 2018
Potrzeba standaryzacji Tworzenie kodu źródłowego (implementacja kodu) to nieodłaczny element procesu wytwarzania systemu wbudowanego Język C jest najczęściej używanym językiem programowania w systemach wbudowanych Jakość programów napisanych w C wyraźnie odzwierciedla różnicę w poziomie umiejętności kodowania między doświadczonymi i niedoświadczonymi programistami. Standaryzacja umożliwia minimalizowanie tej rożnicy! ESCR zgodne z ISO/IEC 25010:2011 Systems and software engineering Systems and software Quality Requirements and Evaluation (SQuaRE) System and software quality models GUT Intel 2015/16 2/40
Standardy języka C dla systemów wbudowanych Język C C90: ISO/IEC 9899:1990 Programming Language C. C99: ISO/IEC 9899:1999 Programming Language C C11: ISO/IEC 9899:2011 Programming Language C Język C++: ISO/IEC 14882:2003 Programming language C++ MISRA C język C zdefiniowany przez The Motor Industry Software Reliability Association (MISRA) MISRA C:1998 MISRA C:2004 MISRA C:2012 GUT Intel 2015/16 3/40
Model jakości oprogramowania Poprawność (correctness) - stopień zgodności programu ze specyfikacja i zaspokojenia celów przedsięwzięcia Wiarygodność (reliability) - stopień, w jakim program wykonuje zamierzone funkcje z wymagana precyzja Wydajność (efficiency) - ilość przetwarzanych zasobów i kodu, które program potrzebuje do wykonywania swoich funkcji Integralność (integrity) - stopień odporności programu lub danych na nieuprawnione użycie lub modyfikację Użyteczność (usability) - wysiłek, który musi być włożony w nauczenie się programu, w jego użycie, przygotowanie danych wejściowych i interpretację danych wyjściowych Łatwość pielęgnacji (maintainability) - wysiłek, który musi być włożony w lokalizację błędów i ich naprawę GUT Intel 2015/16 4/40
Model jakości oprogramowania Elastyczność (flexibility) - wysiłek wymagany dla modyfikacji działajacego programu Testowalność (testability) - wysiłek, który jest potrzebny dla upewnienia się, że program funkcjonuje poprawnie Przenośność (portability) - wysiłek, który może być wymagany dla przeniesienia oprogramowania z jednego systemu sprzętowego lub programowego do drugiego Możliwość powtórnego wykorzystania (reusability) - stopień, w jakim całość lub część oprogramowania może zostać powtórnie wykorzystana w innej aplikacji Łatwość współdziałania (interoperability) - wysiłek, który musi być włożony dla podłaczenia jednego systemu do drugiego GUT Intel 2015/16 5/40
Model jakości oprogramowania (ISO/IEC 25010) GUT Intel 2015/16 6/40
Embedded System development Coding Reference (ESCR) Publikowane przez IPA/SEC (Software Reliability Enhancement Center, Technology Headquarters, Information-technology Promotion Agency, Japan) Zalecenia dotyczace jakości kodu źródłowego w języku C Opracowane w celu ustanowienia pewnej konwencji i promowania standardów kodowania w języku C Zgodne ze standardem C99 i MISRA C Wersja 2.0, będaca rozwinięciem wersji 1.1 https://www.ipa.go.jp/files/000040508.pdf GUT Intel 2015/16 7/40
Dobre praktyki GUT Intel 2015/16 8/40
Inicjalizacja zmiennych Jeśli zmienne sterujace nie zostana zainicjowane, ich wartości staja się niezdefiniowane, a wyniki operacji moga się różnić od spodziewanych. Inicjalizacja musi mieć miejsce w momencie deklaracji lub wprost przed użyciem zmiennej. GUT Intel 2015/16 9/40
Inicjalizacja stałych Stałe musza być zainicjalizowane w momencie deklaracji, ponieważ później nie można juz im przypisać wartości. W języku C brak inicjalizacji przy deklaracji stałej nie powoduje błędu kompilacji (w języku C++ już tak). GUT Intel 2015/16 10/40
Inicjalizacja tablic Tablice o ustalonej liczbie elementów powinny być inicjalizowane taka liczba wartości, ile elementów ma tablica. Jeśli do tablicy o N elementach zostanie zapisanych N znaków, może to spowodować błędne działanie funkcji przetwarzajacej łańcuch znaków. Jeśli dane w tablicy maja być przetwarzane jako łańcuch znaków, należy pamiętać o znaku pustym jako jej ostatnim elemencie. GUT Intel 2015/16 11/40
Zakres obszaru pamięci wskazywanego przez wskaźnik 1. Nie należy dodawać wartości całkowitych do zmiennych wskaźnikowych! Bezpieczniej jest traktować obszar pamięci jak tablicę i zmieniać indeks elementów tablicy (a nie wskaźnik). 2. Nie należy wychodzić poza zakres pamięci zaalokowanej dla tablicy. GUT Intel 2015/16 12/40
Odejmowanie wartości wskaźników od siebie Odejmowanie wartości wskaźników w celu uzyskania wiedzy o liczbie elementów tablicy pomiędzy tymi wskaźnikami dopuszczalne jest tylko w zakresie tej samej tablicy GUT Intel 2015/16 13/40
Porównywanie wartości wskazywanych przez wskaźniki Porównywanie wartości wskazywanych przez wskaźniki dopuszczalne jest jedynie wtedy, gdy obydwa wskaźniki wskazuja na wartości znajdujacej się w tej samej tablicy lub strukturze. W przeciwnym razie wynik takiego porównania jest niezdefiniowany (w ogólnym przypadku). Należy pamiętać, że adresy poczatków tablic sa zależne od platformy! GUT Intel 2015/16 14/40
Liczba zmiennoprzecinkowa a instrukcja iteracyjna Liczba zmiennoprzecinkowa nie powinna być licznikiem (zmienna sterujac a) instrukcji iteracyjnej. Błędy zaokragleń moga się kumulować i doprowadzić do nieprzewidzianego zachowania instrukcji iteracyjnej. GUT Intel 2015/16 15/40
Porównywanie struktur i unii Pamięć struktur i unii może zawierać niewykorzystana przestrzeń adresowa. Wartości zapisane pod niewykorzystanymi adresami nie sa znane. Dlatego też struktury i unie nie powinny być porównywane ze soba za pomoca funkji memcmp. Porównywanie powinno odbywać się pomiędzy poszczególnymi elementami struktur. GUT Intel 2015/16 16/40
Porównywanie z wartościa logiczna Należy unikać sprawdzania prawdziwości wyrażenia: zmienna == TRUE. W języku C wartości TRUE odpowiada każda niezerowa wartość. GUT Intel 2015/16 17/40
Operator warunkowy (?:) Podczas użycia operatora warunkowego (?:) wyrażenie logiczne powinno być zamknięte w nawiasach (), a zwracane wartości powinny być tego samego typu. GUT Intel 2015/16 18/40
Porównywanie zmiennej z licznikiem instrukcji iteracyjnej - typ danych! Porównywanie wartości zmiennych o różnym zakresie wartości może prowadzić do nieprzewidzianego zachowania instrukcji iteracyjnej. GUT Intel 2015/16 19/40
Zgodność typów argumentów i wyniku operacji arytmetycznej W przypadku działać arytmetycznych typ zmiennej, do której zapisywany jest wynik, nie ma znaczenia podczas kompilacji. Precyzja wartości wynikowej zależy tylko od argumentów działania. W celu kontrolowania typu danych należy stosować rzutowanie typów PRZED operacja arytmetyczna. GUT Intel 2015/16 20/40
Zgodność typów argumentów i wyniku operacji arytmetycznej Jawne rzutowanie typów potrzebne jest również wtedy, gdy operacja arytmetyczna dotyczy typów: ze znakiem i bez znaku. Zawsze jednak warto rozważyć najpierw zmianę typu danych. GUT Intel 2015/16 21/40
Ryzyko utraty informacji Jeśli w zmiennej jest zapisywana wartość innego typu, niz ta zmienna, zapisana wartość może ulec zmianie. Nawet jeśli wiemy, że dana konwersja nie jest obarczona ryzykiem informacji, rzutowanie typu powinno być jawne. Zawsze warto upewnić się, czy zapisana wartość zmiennej mieści się w założonym zakresie GUT Warto Intelrozważyć 2015/16 zmianę 22/40 typu zmiennych - często jest to najprostsze
Jednoargumentowy operator - a typ unsigned Nie należy używać jednoargumentowego operatora - przed argumentem typu bez znaku. Np.: instrukcja: if ( - ui < 0 ) nie zwróci wartości true. GUT Intel 2015/16 23/40
Operatory: ~ i << a typ unsigned Jeśli operatory ~ i << stosowane sa w odniesieniu do wartości typu bez znaku, wynik będzie typu signed int. Jeśli zależy nam na wyniku unsigned int, potrzebne jest jawne rzutowanie typu. GUT Intel 2015/16 24/40
Przesunięcie bitowe Drugi (prawostronny) argument operatora przesunięcia bitowego << powinien być większy badź równy zero, ale mniejszy od liczby bitów argumentu z lewej strony. W przeciwnym razie zachowanie programu będzie nieprzewidywalne (zależy od kompilatora). GUT Intel 2015/16 25/40
Sekwencja bitów Sekwencja bitów powinna być definiowana jako liczba typu unsigned. Wynik działania operatorów bitowych na liczbie typu signed może być zależny od kompilatora. GUT Intel 2015/16 26/40
Porównanie: wskaźnik < > 0 Nie ma sensu sprawdzać, czy wskaźnik jest mniejszy lub większy od zera. Jeśli jednym z argumentów porównania jest wskaźnik, kompilator zmieni wartość 0 na null pointer, wówczas wynik porównania może być inny od zakładanego. GUT Intel 2015/16 27/40
Deklaracja funkcji bez parametrów Deklaracja function() może być interpretowana przez niektóre kompilatory jako funkcja o nieznanej liczbie parametrów. Dlatego w przypadku funkcji bez parametrów należy deklarować ja z 1 parametrem void. GUT Intel 2015/16 28/40
Funkcja o zmiennej liczbie parametrów Funkcja nie powinna być deklarowana ze zmienna liczba parametrów. Bez rozumienia procesu przetwarzania takich funkcji dla konkretnej platformy ich użycie może powodować przepełnienie stosu lub inne niespodziewane problemy. GUT Intel 2015/16 29/40
Deklaracja prototypu funkcji Deklaracja prototypu funkcji powinna znajdować się w jednym miejscu, do którego można odnieść się w pliku gdzie znajduje się jej definicja oraz wywołanie. GUT Intel 2015/16 30/40
Zawsze sprawdzaj, czy nie wychodzisz poza zakres adresów elementów tablicy W języku C tablica nie może mieć zmiennej wielkości GUT Intel 2015/16 31/40
Unikaj konstrukcji, które w szczególnych okolicznościach moga spowodować runtime error GUT Intel 2015/16 32/40
Jeśli funkcja zwraca kod błędu, trzeba go jawnie sprawdzić GUT Intel 2015/16 33/40
Nie używaj wywołań rekurencyjnych (MISRA C) Rozmiar stosu używany podczas wywołania rekurencyjnego jest nieprzewidywalny, więc istnieje ryzyko przepełnienia go. GUT Intel 2015/16 34/40
W instrukcjach warunkowych zawsze obsługuj wszystkie możliwe przypadki jeśli brakuje else, nie wiadomo, czy jest to zamierzone, czy programista zapomniał czegoś dopisać nawet jeśli nie potrzebujesz obsługi else, warto wykorzystać ta część GUT Intel 2015/16 35/40 instrukcji do obsługi wyjatków
W instrukcjach warunkowych zawsze obsługuj wszystkie możliwe przypadki GUT Intel 2015/16 36/40
Nie używaj operatora porównania: == lub!= do sprawdzania wartości licznika instrukcji iteracyjnej GUT Intel 2015/16 37/40
Nie łacz w jednej instrukcji przekazania parametru do funkcji i jego modyfikacji Kompilatory nie gwarantuj a kolejności wykonania operacji na parametrach wejściowych funkcji! (side effect problem) GUT Intel 2015/16 38/40
Nie porównuj liczb zmiennoprzecinkowych za pomoca operatora == lub!=! dobrze: f l o a t a = 2. 0 ; f l o a t b = a 3; f l o a t e p s i l o n = 0.0001; i f ( b a 3 < e p s i l o n ) {... } źle: f l o a t a = 2.0 f l o a t b = a 3; i f ( b == a 3) { / / TRUE of FALSE?} GUT Intel 2015/16 39/40
Literatura GUT Intel 2015/16 40/40