Czy jest jakiś powód, aby używać języka C zamiast C ++ do tworzenia aplikacji wbudowanych?

82

Pytanie

Mam dwa kompilatory na moim sprzęcie C ++ i C89

Myślę o użyciu C ++ z klasami, ale bez polimorfizmu (aby uniknąć vtables). Główne powody, dla których chciałbym używać C ++ to:

  • Wolę używać funkcji „inline” zamiast definicji makr.
  • Chciałbym używać przestrzeni nazw, ponieważ prefiksy zaśmiecają kod.
  • Uważam, że C ++ jest trochę bezpieczniejszy, głównie z powodu szablonów i szczegółowego rzutowania.
  • Bardzo lubię przeciążone funkcje i konstruktory (używane do automatycznego rzutowania).

Czy widzisz jakiś powód, aby pozostać przy C89 podczas tworzenia oprogramowania dla bardzo ograniczonego sprzętu (4kb pamięci RAM)?

Wniosek

Dziękuję za odpowiedzi, były naprawdę pomocne!

Przemyślałem temat i pozostanę przy C głównie dlatego, że:

  1. Łatwiej jest przewidzieć rzeczywisty kod w C i jest to naprawdę ważne, jeśli masz tylko 4kb pamięci RAM.
  2. Mój zespół składa się głównie z programistów C, więc zaawansowane funkcje C ++ nie będą często używane.
  3. Znalazłem sposób na wbudowane funkcje w moim kompilatorze C (C89).

Trudno zaakceptować jedną odpowiedź, ponieważ podałeś tak wiele dobrych odpowiedzi. Niestety nie mogę stworzyć wiki i zaakceptować go, więc wybiorę jedną odpowiedź, która zmusiła mnie do myślenia.

Piotr Czapla
źródło
11
Jedna rzecz: zawsze dokładnie wiedz, w jakim języku piszesz. Nie próbuj pisać programu w języku „C / C ++”. Pisz w C lub C ++, wiedząc, jakich funkcji językowych będziesz używać, a których nie.
David Thornley
Zdefiniuj „wbudowany rozwój”
Marco van de Voort
@DavidThornley, możesz mieć rację w przypadku osadzonych przypadków, ale byłem bardzo mile zaskoczony, jak ładnie zmieszany kod C i C ++ gra razem, gdy chcę rozszerzyć popularne aplikacje open source, takie jak Kamailio, z STL. Oficjalnie zachęcam do korzystania z kodu STL i C, ponieważ oferuje on ogromną funkcjonalność i łatwość konserwacji w przyszłości, a jednocześnie stwarza prawie zerowe problemy (brak struktur osadzonych w C ++ jest straszną zbrodnią przeciwko C ++ i powinien zostać naprawiony jak najszybciej ).
user2548100
Do przemyślenia, oto świetny artykuł, w którym projektant i pisarz ZeroMQ omawia, dlaczego żałuje pisania kodu w C ++ zamiast C. Nie tego w ogóle się spodziewałem iz powodów nie znalezionych gdzie indziej na tej stronie. 250bpm.com/blog:4
user2548100

Odpowiedzi:

48

Dwa powody używania C zamiast C ++:

  1. W przypadku wielu procesorów wbudowanych albo nie ma kompilatora C ++, albo trzeba za to dodatkowo zapłacić.
  2. Z mojego doświadczenia wynika, że ​​znaczna część inżynierów oprogramowania wbudowanego ma niewielkie lub żadne doświadczenie w C ++ - albo z powodu (1), albo dlatego, że nie uczy się go na stopniach inżynierii elektronicznej - więc lepiej byłoby trzymać się tego co oni wiedzą.

Również pierwotne pytanie i szereg komentarzy wspominają o 4 Kb pamięci RAM . W przypadku typowego procesora wbudowanego ilość pamięci RAM jest (w większości) niezwiązana z rozmiarem kodu, ponieważ kod jest przechowywany i uruchamiany z pamięci flash.

Z pewnością ilość miejsca do przechowywania kodu jest czymś, o czym należy pamiętać, ale ponieważ na rynku pojawiają się nowe, bardziej pojemne procesory, jest to mniejszy problem niż kiedyś w przypadku wszystkich projektów, z wyjątkiem najbardziej wrażliwych na koszty.

O wykorzystaniu podzbioru C ++ do użytku z systemami wbudowanymi: istnieje teraz standard MISRA C ++ , któremu warto przyjrzeć się.

EDYCJA: Zobacz także to pytanie , które doprowadziło do debaty na temat C vs C ++ dla systemów wbudowanych.

Steve Melnikoff
źródło
2
Zobacz moją dłuższą odpowiedź poniżej: C ++ ma tendencję do bardzo trudnego umieszczania stałych danych we FLASH.
jakobengblom2
3
Potencjalnie dobrym powodem do używania C zamiast C ++ jest standardowy ABI C. Tylko dla kompletności.
Chris Lutz,
66

W przypadku celu o bardzo ograniczonych zasobach, takiego jak 4KB pamięci RAM, przetestowałbym wody z kilkoma próbkami przed podjęciem dużego wysiłku, którego nie można łatwo przenieść z powrotem do czystej implementacji ANSI C.

Grupa robocza Embedded C ++ zaproponowała standardowy podzbiór języka i standardowy podzbiór biblioteki standardowej. Niestety, straciłem z oczu ten wysiłek, gdy zmarł Dziennik użytkownika C. Wygląda na to, że w Wikipedii jest artykuł , a komitet nadal istnieje.

W środowisku osadzonym naprawdę trzeba uważać na alokację pamięci. Aby wymusić tę opiekę, może być konieczne zdefiniowanie globalnego operator new()i jego przyjaciół z czymś, czego nie można nawet powiązać, aby wiedzieć, że nie jest używane. newZ drugiej strony miejsce docelowe może być twoim przyjacielem, jeśli jest używane rozsądnie wraz ze stabilnym, bezpiecznym dla wątków i gwarantowanym schematem alokacji.

Funkcje wbudowane nie będą stanowiły większego problemu, chyba że są na tyle duże, że powinny być funkcjami prawdziwymi. Oczywiście makra, które zostały zastąpione, miały ten sam problem.

Szablony też mogą nie powodować problemu, chyba że ich instancja działa w amoku. Dla każdego szablonu, którego używasz, przeprowadź audyt wygenerowanego kodu (mapa linków może zawierać wystarczające wskazówki), aby upewnić się, że wystąpiły tylko instancje, których zamierzałeś użyć.

Innym problemem, który może się pojawić, jest zgodność z debugerem. Nie jest niczym niezwykłym, że debugger sprzętowy, który może być używany w inny sposób, ma bardzo ograniczoną obsługę interakcji z oryginalnym kodem źródłowym. Jeśli musisz skutecznie debugować w asemblerze, to interesujące zniekształcanie nazw w C ++ może dodać dodatkowe zamieszanie do zadania.

RTTI, dynamiczne rzutowanie, wielokrotne dziedziczenie, ciężki polimorfizm i wyjątki - wszystko to wiąże się z pewnym kosztem czasu wykonania. Niektóre z tych funkcji kosztują cały program, jeśli są używane, inne po prostu zwiększają wagę klas, które ich potrzebują. Poznaj różnicę i mądrze wybieraj zaawansowane funkcje, mając pełną wiedzę przynajmniej na temat pobieżnej analizy kosztów i korzyści.

W małym, wbudowanym środowisku będziesz łączył się bezpośrednio z jądrem czasu rzeczywistego lub działał bezpośrednio na sprzęcie. Tak czy inaczej, będziesz musiał upewnić się, że kod startowy środowiska wykonawczego poprawnie obsługuje określone zadania C ++ związane z uruchamianiem. Może to być tak proste, jak upewnienie się, że używasz odpowiednich opcji konsolidatora, ale ponieważ często ma się bezpośrednią kontrolę nad źródłem do punktu wejścia resetowania zasilania, może być konieczne przeprowadzenie audytu, aby upewnić się, że robi wszystko. Na przykład na platformie ColdFire, nad którą pracowałem, narzędzia deweloperskie były dostarczane z modułem CRT0.S, który zawierał inicjatory C ++, ale był komentowany. Gdybym użył go prosto z pudełka, byłbym zdziwiony globalnymi obiektami, których konstruktorzy w ogóle nie działali.

Ponadto w środowisku osadzonym często konieczne jest zainicjowanie urządzeń sprzętowych, zanim będzie można ich użyć, a jeśli nie ma systemu operacyjnego ani programu ładującego, to robi to Twój kod. Będziesz musiał pamiętać, że konstruktory obiektów globalnych są uruchamiane przed main() wywołaniem, więc będziesz musiał zmodyfikować lokalny CRT0.S (lub jego odpowiednik), aby inicjalizacja sprzętu została wykonana przed wywołaniem samych konstruktorów globalnych. Oczywiście na szczyt main()jest już za późno.

RBerteig
źródło
1
Ten potrzebuje więcej głosów poparcia, niż mogę dać! Świetna odpowiedź.
Harper Shelby
+1, świetna odpowiedź. Ale wydaje mi się, że jedyną instancją szablonu, o którą naprawdę musisz się martwić, jest (stosunkowo rzadka) rekurencyjna forma - w przypadku „zwykłego”, nierekurencyjnego instancji, instancja sprowadza się do kodu, który i tak musiałbyś wpisać ręcznie.
j_random_hacker
2
@j_random_hacker, prawda. Ale nawyk szablonów może prowadzić do sporadycznych niespodzianek, gdy pojawia się druga (lub trzecia) instancja, w przypadku gdy wymuszenie odpowiedniego typu w miejscu użycia mogłoby temu zapobiec. To po prostu coś, na co trzeba uważać.
RBerteig
@RBerteig: Słuszna uwaga, szablony pozwalają na mniej możliwości koercji typu => prawdopodobnie jest tworzonych więcej różnych instancji niż w przypadku kodu innego niż szablon.
j_random_hacker
26

Nie. Podczas programowania osadzonego można uniknąć wszelkich funkcji języka C ++, które mogą powodować problemy (polimorfizm w czasie wykonywania, RTTI itp.). Istnieje społeczność programistów embedded C ++ (pamiętam, że czytałem kolumny programistów embedded używających C ++ w starym dzienniku użytkowników C / C ++) i nie mogę sobie wyobrazić, żeby byli bardzo głośni, gdyby wybór był tak zły.

Harper Shelby
źródło
20

Raport Techniczny na C ++ Wydajność jest doskonałym przewodnikiem dla tego rodzaju rzeczy. Zwróć uwagę, że zawiera on sekcję dotyczącą problemów związanych z programowaniem wbudowanym!

Ponadto ++ na wzmiankę o Embedded C ++ w odpowiedziach. Standard nie jest w 100% zgodny z moim gustem, ale jest dobrym punktem odniesienia przy podejmowaniu decyzji, które części C ++ możesz porzucić.

Podczas programowania dla małych platform wyłączamy wyjątki i RTTI, unikamy dziedziczenia wirtualnego i zwracamy szczególną uwagę na liczbę funkcji wirtualnych, które mamy w pobliżu.

Twój przyjaciel jest jednak mapą konsolidatora: sprawdzaj ją często, a szybko zauważysz źródła kodu i pamięć statyczną.

Następnie obowiązują standardowe uwagi dotyczące wykorzystania pamięci dynamicznej: w środowisku tak ograniczonym, jak to, o którym wspomniałeś, możesz w ogóle nie używać alokacji dynamicznych. Czasami możesz uciec z pulami pamięci dla małych alokacji dynamicznych lub alokacją opartą na ramkach, gdzie wstępnie przydzielasz blok i wyrzucasz całość później.

leander
źródło
16

Zalecam używanie kompilatora C ++, ale ograniczam użycie specyficznych funkcji C ++. Możesz programować jak C w C ++ (środowisko wykonawcze C jest zawarte w C ++, chociaż w większości wbudowanych aplikacji i tak nie korzystasz z biblioteki standardowej).

Możesz śmiało używać klas C ++ itp. Po prostu

  • Ogranicz korzystanie z funkcji wirtualnych (jak powiedziałeś)
  • Ogranicz korzystanie z szablonów
  • W przypadku platformy osadzonej należy zastąpić operatora new i / lub użyć miejsca docelowego new do alokacji pamięci.
arke
źródło
8
Oczywiście, jeśli już w zasadzie piszesz C, równie dobrze możesz zrobić to oficjalnie.
Chuck
6
Dlaczego ograniczasz korzystanie z szablonów? Pomyślałem, że funkcje szablonów mogą być naprawdę pomocne w systemach wbudowanych, na przykład do rozwijania pętli.
Piotr Czapla
1
Nadal możesz używać szablonów, ale byłbym z nimi bardzo ostrożny, ponieważ mogą szybko zwiększyć rozmiar wyjściowego pliku binarnego. Oczywiście, jeśli twój kod jest uruchamiany bezpośrednio z ROM lub podobnego i masz wolne miejsce w ROM, to oczywiście, ale poza tym musisz uważać na to, co robisz z szablonami (każda instancja szablonu to w zasadzie cały kod oparty na szablonie jest ponownie kopiowany w ostatecznym pliku wykonywalnym w najgorszym przypadku).
arke
14

Jako inżynier oprogramowania układowego / systemów wbudowanych mogę wam powiedzieć, dlaczego C jest nadal najlepszym wyborem w stosunku do C ++ i tak, biegle posługuję się obydwoma.

1) Niektóre cele, które opracowujemy, mają 64 kB pamięci RAM zarówno dla kodu, jak i danych, więc musisz upewnić się, że liczy się każdy bajt, i tak, zajmowałem się optymalizacją kodu, aby zaoszczędzić 4 bajty, które kosztowały mnie 2 godziny, i to jest w 2008.

2) Każda funkcja biblioteki C jest sprawdzana, zanim umieścimy ją w ostatecznym kodzie, ze względu na ograniczenie rozmiaru, więc wolimy, aby ludzie nie używali dzielenia (brak dzielnika sprzętowego, więc potrzebna jest duża biblioteka), malloc (ponieważ nie mamy sterty , cała pamięć jest przydzielana z bufora danych w 512-bajtowej porcji i musi zostać przejrzana przez kod) lub inne praktyki zorientowane obiektowo, które niosą ze sobą dużą karę. Pamiętaj, liczy się każda funkcja biblioteki, której używasz.

3) Słyszałeś kiedyś o nakładce terminu? masz tak mało miejsca na kod, że czasami musisz zamienić coś z innym zestawem kodu. Jeśli wywołujesz funkcję biblioteczną, wówczas funkcja biblioteki musi być rezydentna. Jeśli używasz go tylko w funkcji nakładki, marnujesz dużo miejsca, polegając na zbyt wielu metodach obiektowych. Więc nie zakładaj żadnej funkcji biblioteki C, nie mówiąc już o akceptacji C ++.

4) Odlewanie, a nawet pakowanie (gdzie niewyrównana struktura danych przekracza granice słów) jest potrzebne ze względu na ograniczoną konstrukcję sprzętu (np. Silnik ECC, który jest podłączony w określony sposób) lub w celu radzenia sobie z błędem sprzętowym. Nie możesz zakładać zbyt wiele w sposób niejawny, więc dlaczego obiekt zbytnio go orientuje?

5) Najgorszy scenariusz: wyeliminowanie niektórych metod obiektowych zmusi programistę do myślenia, zanim użyją zasobów, które mogą eksplodować (tj. Przydzielenie 512 bajtów na stos zamiast z bufora danych) i zapobiegnie niektórym z potencjalnych najgorszych scenariuszy, które nie są testowane ani nie eliminują całej ścieżki kodu.

6) Używamy dużej abstrakcji, aby chronić sprzęt przed oprogramowaniem i uczynić kod możliwie najbardziej przenośnym i przyjaznym dla symulacji. Dostęp sprzętowy musi być opakowany w makro lub funkcję wbudowaną, które są warunkowo kompilowane między różnymi platformami, typ danych musi być rzutowany jako rozmiar bajtu, a nie określony cel, bezpośrednie użycie wskaźnika jest niedozwolone (ponieważ niektóre platformy zakładają, że we / wy mapowane w pamięci jest tak samo jak pamięć danych) itp.

Mogę wymyślić więcej, ale masz pomysł. My, ludzie zajmujący się oprogramowaniem sprzętowym, mamy szkolenie zorientowane obiektowo, ale zadanie systemu wbudowanego może być tak zorientowane sprzętowo i na niskim poziomie, że nie jest z natury wysokopoziomowe ani abstrakcyjne.

Przy okazji, każda praca oprogramowania układowego, w której byłem, wykorzystuje kontrolę źródła, nie wiem, skąd wziął się ten pomysł.

- jakiś gość od oprogramowania firmy SanDisk.

Shing Wong
źródło
jeszcze na początku lat 90. nakładka była bardzo popularną techniką (przynajmniej w świecie DOS)
psihodelia
Zalety Shing. C ++ czuje się jak zapaśnik sumo w budce telefonicznej w projektach, w których funkcjonalność jest ograniczona, a zasoby są jeszcze bardziej ograniczone.
4
Uważam, że ta odpowiedź jest bardzo subiektywna i nie dostarcza konkretnego uzasadnienia.
Venemo
1
C ++ niekoniecznie oznacza „obiektowy”.
Martin Bonner obsługuje Monikę
1
Po prostu nie jest prawdą, że zadania systemu wbudowanego nie są z natury abstrakcyjne. Powiedziałeś to sam w punkcie 6): „używamy dużo abstrakcji, aby zabezpieczyć hw przed sw i uczynić kod tak przenośnym, jak to tylko możliwe” :-) BTW: „abstrakcja” niekoniecznie oznacza „polimorfizm”.
Daniele Pallastrelli,
9

Moje osobiste preferencje to C, ponieważ:

  • Wiem, co robi (i kosztuje) każdy wiersz kodu
  • Nie znam języka C ++ na tyle dobrze, aby wiedzieć, co robi (i kosztuje) każdy wiersz kodu

Dlaczego ludzie to mówią? Ty nie wiesz, co każda linia C robi chyba sprawdzić wyjście asm. To samo dotyczy C ++.

Na przykład, co asm daje to niewinne stwierdzenie:

a[i] = b[j] * c[k];

Wygląda to dość niewinnie, ale kompilator oparty na gcc tworzy ten asm dla 8-bitowego mikro

CLRF 0x1f, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1f, F, ACCESS
MOVWF 0x1e, ACCESS
MOVLW 0xf9
MOVF 0xfdb, W, ACCESS
ADDWF 0x1e, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfa
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1f, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x1c
NOP
MOVFF 0xfef, 0x1d
NOP
MOVLW 0x1
CLRF 0x1b, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1b, F, ACCESS
MOVWF 0x1a, ACCESS
MOVLW 0xfb
MOVF 0xfdb, W, ACCESS
ADDWF 0x1a, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfc
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1b, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x18
NOP
MOVFF 0xfef, 0x19
NOP
MOVFF 0x18, 0x8
NOP
MOVFF 0x19, 0x9
NOP
MOVFF 0x1c, 0xd
NOP
MOVFF 0x1d, 0xe
NOP
CALL 0x2142, 0
NOP
MOVFF 0x6, 0x16
NOP
MOVFF 0x7, 0x17
NOP
CLRF 0x15, ACCESS
RLCF 0xfdf, W, ACCESS
ANDLW 0xfe
RLCF 0x15, F, ACCESS
MOVWF 0x14, ACCESS
MOVLW 0xfd
MOVF 0xfdb, W, ACCESS
ADDWF 0x14, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfe
MOVF 0xfdb, W, ACCESS
ADDWFC 0x15, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0x16, 0xfee
NOP
MOVFF 0x17, 0xfed
NOP

Liczba wyprodukowanych instrukcji zależy w dużej mierze od:

  • Rozmiary a, b i c.
  • czy te wskaźniki są przechowywane na stosie, czy są globalne
  • czy i, j oraz k są na stosie, czy są globalne

Jest to szczególnie prawdziwe w małym, wbudowanym świecie, w którym procesory po prostu nie są skonfigurowane do obsługi C.Więc moja odpowiedź byłaby taka, że ​​C i C ++ są tak samo złe, jak siebie nawzajem, chyba że zawsze sprawdzasz wyjście asm, w takim przypadku są tak samo dobrzy jak inni.

Hugo

Magnes rakietowy
źródło
2
Zauważ również, że w środku wszystkiego, co faktycznie wywołuje funkcję mnożenia, znajduje się instrukcja call. Cały ten kod nie jest nawet instrukcją mnożenia!
Rocketmagnet
Ktoś zaznajomiony z mikro zwykle znałby prosty sposób przetwarzania każdej części kodu w C z osobna, a przyzwoity kompilator nie powinien tworzyć gorszego kodu. Jedynym sposobem wydajnego przetworzenia powyższego wyrażenia byłoby przyjęcie założeń, które mogą nie być odpowiednie dla kompilatora C.
supercat
8

Słyszałem, że niektórzy wolą C do pracy osadzonej ze względu na fakt, że jest prostszy, a zatem łatwiejszy do przewidzenia faktycznego kodu, który zostanie wygenerowany.

Osobiście uważam, że pisanie C ++ w stylu C (przy użyciu szablonów dla bezpieczeństwa typów) dałoby ci wiele korzyści i nie widzę żadnego prawdziwego powodu, aby tego nie robić.

user21714
źródło
+1, przejrzystość jest zawsze ważna i prawdopodobnie bardziej odpowiednia dla ograniczonego środowiska z (prawdopodobnie) ograniczonymi narzędziami do debugowania.
j_random_hacker
7

Nie widzę powodu, aby używać C zamiast C ++. Cokolwiek potrafisz zrobić w C, możesz to również zrobić w C ++. Jeśli chcesz uniknąć narzutów VMT, nie używaj metod wirtualnych i polimorfizmu.

Jednak C ++ może dostarczyć bardzo przydatnych idiomów bez narzutu. Jednym z moich ulubionych jest RAII. Zajęcia niekoniecznie są drogie pod względem pamięci lub wydajności ...

Cătălin Pitiș
źródło
6

Napisałem kod dla paltform osadzonego w ARM7 w IAR Workbench. Zdecydowanie zalecam korzystanie z szablonów w celu optymalizacji czasu kompilacji i przewidywania ścieżki. Unikaj dynamicznego rzucania jak zarazy. Użyj cech / zasad na swoją korzyść, zgodnie z zaleceniami w książce Andrei Alexandrescu, Modern C ++ design .

Wiem, że może to być trudne do nauczenia, ale jestem też pewien, że Twój produkt skorzysta na takim podejściu.

GregC
źródło
5

Dobry powód, a czasami jedyny, jest taki, że wciąż nie ma kompilatora C ++ dla konkretnego systemu wbudowanego. Tak jest na przykład w przypadku mikrokontrolerów Microchip PIC . Są bardzo łatwe do napisania i mają darmowy kompilator C (właściwie niewielki wariant C), ale w zasięgu wzroku nie ma kompilatora C ++.

shoosh
źródło
1
Comeau Computing ( comeaucomputing.com ) sprzedaje kompilator C ++, który kompiluje się do C.
Thomas L Holaday
3
Eww. Ta strona miała ochotę zwymiotować.
shoosh
@shoosh: Tak, projekt strony jest okropny. Jednak sam kompilator jest uważany za lidera w tej dziedzinie, przynajmniej jeśli chodzi o zgodność ze standardami (nie mam informacji o wydajności).
j_random_hacker
Ta strona internetowa sprawia, że ​​czuję się jak uwięziony w żywej, oddychającej i BARDZO wściekłej sałatce owocowej.
Tim Post
5

W przypadku systemu ograniczonego do 4K pamięci RAM użyłbym C, a nie C ++, tylko po to, aby mieć pewność, że zobaczysz wszystko, co się dzieje. C ++ polega na tym, że bardzo łatwo jest użyć znacznie większej ilości zasobów (zarówno procesora, jak i pamięci), niż wygląda na to, by spojrzeć na kod. (Och, po prostu utworzę kolejny BlerfObject, aby to zrobić ... ups! Z pamięci!)

Możesz to zrobić w C ++, jak już wspomniano (bez RTTI, bez vtables itp.), Ale spędzisz tyle czasu, aby upewnić się, że korzystanie z C ++ nie ucieknie od Ciebie, jak w przypadku odpowiednika w C .

Michael Kohne
źródło
2
Twoje ostatnie zdanie jest słuszne, ale nie ma znaczenia, ponieważ C ++ oferuje inne zalety w porównaniu z C, które (mogą) przechylić szalę. Piotr wymienił już niektóre z tych (zerowych) zalet.
Konrad Rudolph
5

Umysł ludzki radzi sobie ze złożonością, oceniając jak najwięcej, a następnie decydując, na czym jest ważne, aby się skupić, a resztę odrzuca lub amortyzuje. To jest cała podstawa budowania marki w marketingu, a przede wszystkim ikony.

Aby zwalczyć tę tendencję, wolę C od C ++, ponieważ zmusza cię to do myślenia o swoim kodzie i jego ściślejszej interakcji ze sprzętem - nieustannie blisko.

Z długiego doświadczenia jestem przekonany, że C zmusza cię do wymyślania lepszych rozwiązań problemów, po części poprzez zejście z drogi i nie zmuszanie cię do marnowania dużo czasu na spełnienie ograniczenia, który niektórzy kompilatorzy uważali za dobry pomysł lub dowiedzieć się, co się dzieje „pod kołdrą”.

W tym duchu języki niskiego poziomu, takie jak C, sprawiają, że spędzasz dużo czasu skupiając się na sprzęcie i budowaniu dobrych pakietów struktury danych / algorytmów, podczas gdy języki wysokiego poziomu sprawiają, że spędzasz dużo czasu na drapaniu się po głowie, zastanawiając się, co się tam dzieje. i dlaczego nie możesz zrobić czegoś całkiem rozsądnego w swoim konkretnym kontekście i środowisku. Przekonanie kompilatora do złożenia (mocne pisanie jest największym winowajcą) NIE jest produktywnym wykorzystaniem czasu.

Pewnie dobrze pasuję do formy programisty - lubię sterowanie. Moim zdaniem to nie jest wada osobowości programisty. Płacą nam za to kontrola. Mówiąc dokładniej, BEZPRAWIDŁOWA kontrola. C daje znacznie większą kontrolę niż C ++.

Cofnij
źródło
Martin Sistrik, autor ZeroMQ, wskazał prawie to samo w swojej dyskusji, dlaczego chciałby teraz napisać ZeroMQ w C, zamiast C ++. Sprawdź to 250bpm.com/blog:8
user2548100
3

Osobiście z 4kb pamięci powiedziałbym, że nie uzyskasz dużo więcej kilometrów z C ++, więc po prostu wybierz taką, która wydaje się najlepszą kombinacją kompilator / środowisko uruchomieniowe do tego zadania, ponieważ język prawdopodobnie nie będzie miał większego znaczenia.

Zauważ, że i tak nie chodzi tylko o język, ponieważ liczy się również biblioteka. Często biblioteki C mają nieco mniejszy minimalny rozmiar, ale mogę sobie wyobrazić, że biblioteka C ++ przeznaczona do programowania osadzonego jest ograniczona, więc koniecznie przetestuj.

Marco van de Voort
źródło
2

C wygrywa w przenośności - ponieważ jest mniej niejednoznaczny w specyfikacji językowej; dlatego oferuje znacznie lepszą przenośność i elastyczność między różnymi kompilatorami itp. (mniej problemów).

Jeśli nie zamierzasz wykorzystywać funkcji C ++ do zaspokojenia potrzeb, wybierz C.

Oliver
źródło
To, czy język jest jednoznaczny, zależy od tego, czy uważamy go za specyfikację rzeczy, które kiedyś były uważane za zdrowy rozsądek, ale obecnie tak nie jest [np. Kompilator 32-bitowego sprzętu z dopełnieniem do dwóch z cichym zawijaniem powinien przetwarzać coś takiego unsigned mul(unsigned short x, unsigned short y) { return x*y;}jak nie skutki uboczne, nawet jeśli produkt przekracza 2147483647, lub które należy uznać void get_float_bits(float *fp, uint32_t n) { *(uint32_t)fp = n; }za potencjalnie zmieniające wartość a float].
supercat
2

Czy widzisz jakiś powód, aby pozostać przy C89 podczas tworzenia oprogramowania dla bardzo ograniczonego sprzętu (4kb pamięci RAM)?

Osobiście, jeśli chodzi o aplikacje wbudowane (kiedy mówię o osadzonych, nie mam na myśli dzisiejszych urządzeń wbudowanych winCE, iPhone itp.). Mam na myśli urządzenia o ograniczonych zasobach. Wolę C, chociaż całkiem sporo pracowałem z C ++.

Na przykład urządzenie, o którym mówisz, ma 4kb pamięci RAM, cóż, właśnie z tego powodu nie rozważałbym C ++. Jasne, możesz zaprojektować coś małego za pomocą C ++ i ograniczyć użycie tego w swojej aplikacji, tak jak sugerowały to inne posty, ale C ++ „może” potencjalnie skomplikować / rozdęć aplikację pod osłonami.

Czy zamierzasz łączyć się statycznie? Możesz chcieć porównać statyczną aplikację fikcyjną przy użyciu c ++ vs c. To może doprowadzić do rozważenia zamiast tego C. Z drugiej strony, jeśli jesteś w stanie zbudować aplikację C ++ w ramach swoich wymagań dotyczących pamięci, zrób to.

IMHO, Ogólnie rzecz biorąc, w aplikacjach wbudowanych lubię wiedzieć wszystko, co się dzieje. Kto korzysta z pamięci / zasobów systemowych, ile i dlaczego? Kiedy je uwalniają?

Podczas programowania dla celu z X ilością zasobów, procesorem, pamięcią itp. Staram się pozostać po niższej stronie korzystania z tych zasobów, ponieważ nigdy nie wiesz, jakie przyszłe wymagania się pojawią, dlatego musisz dodać więcej kodu do projektu, który miał być prostą, małą aplikacją, ale w końcu stał się znacznie większy.

Steve Lazaridis
źródło
1
Zdecydowanie porównuję te dwa kompilatory. (przy okazji. Nie mogę łączyć dynamicznie, bo nie ma systemu operacyjnego)
Piotr Czapla
2

O moim wyborze decyduje zazwyczaj biblioteka C, z której zdecydujemy się skorzystać, która jest wybierana na podstawie tego, co urządzenie musi zrobić. Tak więc 9/10 razy… kończy się na uclibc lub newlib i C. Jądro, którego używamy, ma na to duży wpływ, lub jeśli piszemy własne jądro.

To także wybór wspólnej płaszczyzny. Większość dobrych programistów C nie ma problemu z używaniem C ++ (mimo że przez cały czas wielu narzeka, że ​​go używają) .. ale nie znalazłem odwrotności (z mojego doświadczenia).

W projekcie, nad którym pracujemy (który wymaga od podstaw jądra), większość rzeczy jest wykonywana w C, jednak mały stos sieciowy został zaimplementowany w C ++, ponieważ po prostu łatwiej i mniej problematycznie było zaimplementować sieć w C ++.

Efekt końcowy jest taki, że urządzenie albo będzie działać i przejdzie testy akceptacyjne, albo nie. Jeśli możesz zaimplementować ograniczenia foo na stosie xx i yy sterty za pomocą języka z, idź do tego i użyj wszystkiego, co sprawia, że ​​jesteś bardziej produktywny.

Moje osobiste preferencje to C, ponieważ:

  • Wiem, co robi (i kosztuje) każdy wiersz kodu
  • Nie znam języka C ++ na tyle dobrze, aby wiedzieć, co robi (i kosztuje) każdy wiersz kodu

Tak, czuję się dobrze z C ++, ale nie znam go tak dobrze, jak standardowe C.

Teraz, jeśli możesz powiedzieć coś odwrotnego, użyj tego, co wiesz :) Jeśli to działa, przechodzi testy itp. W czym problem?

Tim Post
źródło
2
> # Wiem, co robi (i kosztuje) każdy wiersz kodu Po napisaniu kompilatorów nie byłbym tego taki pewien ... dobry kompilator C może zrobić z twoim kodem dość zaskakujące rzeczy, ponieważ ma ładny globalny przegląd rzeczy. Nie kompiluje się linia po linii.
jakobengblom2
@ jakobengblom2: W przypadku programowania osadzonego posiadanie stałej wydajności jest często ważniejsze niż maksymalna wydajność. Jeśli ktoś próbuje określić, czy fragment kodu spełnia wymagania czasowe, posiadanie kompilatora wykorzystującego optymalizacje, które będą użyteczne w „testowym” oprogramowaniu układowym, które nie będzie działać w prawdziwym oprogramowaniu, może być mniej niż pomocne.
supercat
2

Ile masz ROM / FLASH?

4kB pamięci RAM może nadal oznaczać, że istnieją setki kilobajtów pamięci FLASH do przechowywania rzeczywistego kodu i danych statycznych. Pamięć RAM o tym rozmiarze jest zwykle przeznaczona tylko dla zmiennych, a jeśli będziesz ostrożny z tymi, możesz zmieścić w pamięci dość duży program pod względem linii kodu.

Jednak C ++ ma tendencję do utrudniania umieszczania kodu i danych w pamięci FLASH ze względu na reguły konstrukcji obiektów w czasie wykonywania. W języku C stałą strukturę można łatwo umieścić w pamięci FLASH i uzyskać do niej dostęp jako obiekt stałej sprzętowej. W C ++ stały obiekt wymagałby od kompilatora oceny konstruktora w czasie kompilacji, co moim zdaniem wykracza poza to, co może zrobić kompilator C ++ (teoretycznie można to zrobić, ale w praktyce jest to bardzo trudne) .

Tak więc w środowisku typu „mała pamięć RAM”, „duży FLASH” mógłbym używać C. każdego dnia. Zauważ, że dobrym wyborem pośrednim jest C99, który ma większość fajnych funkcji C ++ dla kodu nieopartego na klasach.

jakobengblom2
źródło
3
Czy jest jakiś powód, dla którego ta sama struktura, która byłaby umieszczona w pamięci Flash w C, nie trafiłaby również do Flasha w C ++? Nie musisz dodawać konstruktora do swojej struktury w C ++.
jalf
1

Ogólnie nie. C ++ to super zestaw C. Byłoby to szczególnie prawdziwe w przypadku nowych projektów.

Jesteś na dobrej drodze do unikania konstrukcji C ++, które mogą być kosztowne pod względem czasu procesora i zużycia pamięci.

Zwróć uwagę, że niektóre rzeczy, takie jak polimorfizm, mogą być bardzo cenne - są zasadniczo wskaźnikami funkcji. Jeśli uznasz, że ich potrzebujesz, korzystaj z nich - mądrze.

Ponadto dobra (dobrze zaprojektowana) obsługa wyjątków może sprawić, że osadzona aplikacja będzie bardziej niezawodna niż aplikacja, która obsługuje rzeczy z tradycyjnymi kodami błędów.

Foredecker
źródło
2
C ++ nie jest, ściśle mówiąc, ścisłym nadzbiorem języka C, ale ten konkretny szczegół nie jest szczególnie istotny w tym kontekście.
Arafangion,
1

W przypadku problemu z alokacją pamięci mogę polecić użycie platformy Quantum i jej podejścia do automatu stanowego, ponieważ przydziela wszystko, czego potrzebujesz w czasie inicjalizacji. Pomaga również złagodzić problemy związane z rywalizacją.

Ten produkt działa w językach C i C ++.

GregC
źródło
1

Niektórzy twierdzą, że kompilatory C mogą generować znacznie wydajniejszy kod, ponieważ nie muszą obsługiwać zaawansowanych funkcji C ++ i dlatego mogą być bardziej agresywne w swoich optymalizacjach.

Oczywiście w tym przypadku możesz chcieć przetestować dwa konkretne kompilatory.

Yishai
źródło
1
Powiązane: Słowo kluczowe ogranicz jest, o ile wiem, jedyną konstrukcją C związaną z optymalizacją, której brakuje w C ++ (również C ++ 11).
Johan Lundberg
1

Jedynym powodem, dla którego wolisz C IMHO, byłoby to, że kompilator C ++ dla twojej platformy nie jest w dobrym stanie (błędny, słaba optymalizacja itp.).

Nemanja Trifunovic
źródło
A co z wykorzystaniem pamięci / zasobów?
Steve Lazaridis
Co z tym? Nie ma powodu, aby kompilator C ++ tworzył mniej wydajny kod niż C, z wyjątkiem sytuacji, gdy kod używa RTTI, czego nikt nie robi w systemach wbudowanych.
Nemanja Trifunovic
1

Masz inline w C99. Może lubisz lekarzy, ale kwestia właściwego doboru lekarzy może być kłopotliwa. Jeśli jedynym powodem, dla którego nie należy używać C, są przestrzenie nazw, naprawdę trzymałbym się C89. Dzieje się tak, ponieważ możesz chcieć przenieść go na nieco inną wbudowaną platformę. Możesz później zacząć pisać w C ++ na tym samym kodzie. Ale uważaj na następujące, gdzie C ++ NIE jest nadzbiorem C. Wiem, że powiedziałeś, że masz kompilator C89, ale i tak czy to porównanie C ++ z C99, ponieważ na przykład pierwszy element jest prawdziwy dla dowolnego C od K&R.

sizeof 'a' > 1 w C, nie w C ++. W C masz tablice o zmiennej długości VLA. Przykład: func (int i) {int a [i] . W C masz zmienne składowe tablicy VAM. Przykład: struct {int b; int m [];} .

hept
źródło
1
Nie. Chcę wspomnieć, że w C masz (sizeof 'a') == sizeof (int). Podczas gdy w C ++ masz to 1 == sizeof 'a'
hept
1
Nie wspominając o „int * a; ...; a = (int *) malloc (size * sizeof (int));” to sposób na przydzielenie pamięci, który działa w C i C ++ i nie powinien być używany w żadnym z nich. Użyj albo "a = malloc (size * sizeof (int));" lub „vector <int> a (rozmiar)”; lub nawet "int * a = new int [size];" zamiast.
David Thornley
1
Nie rozumiem co do lekarzy. Chodzi o to, że sprawiają, że reszta kodu jest znacznie mniej bałaganiarska.
jalf
1
+1, nie jestem pewien, dlaczego ten post otrzymał taki zły rap. Ale zgadzam się z jalfem, destruktory znacznie upraszczają kod, gdy są używane we właściwy (RAII) sposób. (Można powiedzieć, że "pracują za kulisami", ale robią tylko rzeczy, które i tak
robiłby
1
Myślę, że rzeczy, na które zwracam uwagę, są bardzo istotne dla pytania. Trzymam się też stwierdzenia, że ​​lekarz może być trudny, a powodem jest właśnie to, że dzieje się to automatycznie. Dostałem punkty minus - to naprawdę harch. Chyba dlatego, że nie mówię „TAK, GO C ++ to świetne”.
hept
1

To zależy od kompilatora.

Nie wszystkie wbudowane kompilatory implementują całe C ++, a nawet jeśli to robią, mogą nie być dobre w unikaniu nadmiaru kodu (co jest zawsze ryzykiem w przypadku szablonów). Przetestuj go z kilkoma mniejszymi programami i sprawdź, czy nie napotkasz żadnych problemów.

Ale biorąc pod uwagę dobry kompilator, nie, nie ma powodu, aby nie używać C ++.

jalf
źródło
1

Chcę tylko powiedzieć, że nie ma systemu z „NIEOGRANICZONYMI” zasobami. Wszystko na tym świecie jest ograniczone i KAŻDA aplikacja powinna uwzględniać wykorzystanie zasobów, niezależnie od tego, czy jest to ASM, C, JAVA czy JavaScript. Manekiny, które przydzielają kilka MB "dla pewności", sprawiają, że iPhone 7, Pixel i inne urządzenia są wyjątkowo obciążone. Nieważne, czy masz 4kb czy 40 Gb.

Ale z drugiej strony, aby przeciwstawić się marnotrawieniu zasobów - jest czas potrzebny na ich oszczędzenie. Jeśli potrzeba 1 tygodnia więcej, aby napisać prostą rzecz w C, aby zaoszczędzić kilka taktów i kilka bajtów, zamiast używać C ++ już zaimplementowanego, przetestowanego i rozpowszechnionego. Po co się męczyć? To jak kupowanie koncentratora USB. tak, możesz to zrobić sam, ale czy będzie lepiej? bardziej wiarygodne? taniej, jeśli liczysz swój czas?

Na marginesie - nawet moc z gniazdka nie jest nieograniczona. Spróbuj zbadać, skąd się bierze, a zobaczysz, że głównie coś pali. Prawo energii i materiału jest nadal aktualne: żaden materiał ani energia nie pojawia się ani nie znika, ale raczej się przekształca.

Alex Paniutin
źródło
0

Właśnie znalazłem przykład, jak używać ISO C ++ do programowania systemów wbudowanych, który może być interesujący dla kogoś, kto podejmuje decyzję za każdym razem, gdy używa C ++ lub C.

Został dostarczony przez Bjarne Stroustrup na jego stronie internetowej :

Aby zobaczyć, jak ISO C ++ można wykorzystać do programowania poważnych systemów wbudowanych, zobacz standardy kodowania C ++ pojazdów lotniczych JSF .

Piotr Czapla
źródło
Cóż, latające rzeczy mają zwykle procesory PPC z gigabajtami pamięci RAM. Nie jest to przeciętny system wbudowany o ograniczonych zasobach.
jakobengblom2
0

Różne odpowiedzi dotyczą innego aspektu pytania:

„malloc”

Niektóre poprzednie odpowiedzi dużo o tym mówią. Dlaczego w ogóle myślisz, że to połączenie istnieje? W przypadku naprawdę małej platformy malloc jest zwykle niedostępny lub zdecydowanie opcjonalny. Wdrożenie dynamicznej alokacji pamięci ma sens, gdy masz RTOS w dolnej części systemu - ale do tego czasu jest to czysto niebezpieczne.

Bez niej można zajść bardzo daleko. Pomyśl tylko o wszystkich starych programach FORTRAN, które nie miały nawet odpowiedniego stosu dla zmiennych lokalnych ...

jakobengblom2
źródło
0

Na całym świecie jest wielu różnych producentów kontrolerów, a kiedy przyjrzysz się ich projektom i zestawom instrukcji, których należy użyć do skonfigurowania, możesz mieć wiele problemów. Główną wadą języka asemblera jest zależność od maszyny / architektury. To naprawdę ogromne wymaganie od programisty, aby zapamiętał wszystkie zawarte tam instrukcje, aby wykonać kodowanie dla różnych kontrolerów. Dlatego C stał się bardziej popularny w programowaniu wbudowanym, ponieważ C jest wystarczająco wysoki, aby wyodrębnić algorytmy i struktury danych ze szczegółów zależnych od sprzętu, dzięki czemu kod źródłowy można przenosić na szeroką gamę docelowego sprzętu, język niezależny od architektury i bardzo łatwy do konwertować i utrzymywać kod. Ale widzimy niektóre języki wysokiego poziomu (zorientowane obiektowo), takie jak C, C ++, Python, Java itp.

mohammed_thoyyib_tk
źródło
0

Polecam C ++ z ograniczeniami i uwagami.

  1. Czas na wprowadzenie na rynek i łatwość konserwacji. Programowanie w C ++ jest łatwiejsze i szybsze. Więc jeśli jesteś w fazie projektowania, wybierz kontroler wystarczająco dobry, aby używać C ++. (Należy pamiętać, że niektóre rynki o dużym wolumenie wymagają możliwie najniższych kosztów, na których nie można dokonać tego wyboru).

  2. Prędkość. C może być szybsze niż C ++, ale upewnij się, że przyrost szybkości nie jest duży. Możesz więc przejść do C ++. Opracuj swoje algorytmy, przetestuj je i przyspiesz je tylko w razie potrzeby (!). Użyj profilerów, aby wskazać wąskie gardła i przepisać je w zewnętrzny sposób "C" , aby osiągnąć prędkość C. (Jeśli nadal wolno zaimplementuj tę część w ASM)

  3. Rozmiar binarny. Kody C ++ są większe, ale tutaj jest świetna odpowiedź, która wyjaśnia szczegóły. Rozmiar skompilowanego pliku binarnego danego kodu C będzie taki sam, niezależnie od tego, czy został skompilowany przy użyciu kompilatora C czy C ++. „Rozmiar pliku wykonywalnego nie jest prawie związany z językiem, ale z bibliotekami, które dołączasz do projektu”. Przejść z C ++, ale uniknąć zaawansowane funkcje, jak streams, string, new, virtualfunkcje itp przeglądu wszystkich funkcji biblioteki przed pozwalając im w ostatecznym kodzie, ze względu na ograniczenia wielkości (na podstawie tej odpowiedzi)

betontalpfa
źródło