Jak powinienem układać złożone projekty w C? [Zamknięte]

79

Mam niewiele więcej niż umiejętności na poziomie C na poziomie początkującym i chciałbym wiedzieć, czy istnieją jakieś „standardy” dotyczące struktury nieco złożonej aplikacji w języku C. Nawet te oparte na GUI.

Zawsze korzystałem z paradygmatu OO w Javie i PHP, a teraz, gdy chcę się uczyć C, obawiam się, że mogę źle ukształtować moje aplikacje. Nie mam pojęcia, które wytyczne należy przestrzegać, aby mieć modułowość, oddzielenie i suchość w języku proceduralnym.

Czy masz jakieś sugestie do czytania? Nie mogłem znaleźć żadnego frameworka aplikacji dla C, nawet jeśli nie używam frameworków, zawsze znajdowałem fajne pomysły, przeglądając ich kod.

Stephen
źródło
3
Filozofia Uniksa jest bardzo przydatna przy organizowaniu dużych projektów: http://www.faqs.org/docs/artu/ch01s06.html
nowośćUsuń

Odpowiedzi:

51

Kluczem jest modułowość. Jest to łatwiejsze do zaprojektowania, wdrożenia, kompilacji i utrzymania.

  • Zidentyfikuj moduły w swojej aplikacji, takie jak klasy w aplikacji obiektowej.
  • Oddzielny interfejs i implementacja dla każdego modułu, umieszczaj w interfejsie tylko to, co jest potrzebne innym modułom. Pamiętaj, że w C nie ma przestrzeni nazw, więc musisz uczynić wszystko w swoich interfejsach unikalnymi (np. Z prefiksem).
  • Ukryj zmienne globalne w implementacji i użyj funkcji akcesorów do odczytu / zapisu.
  • Nie myśl w kategoriach dziedziczenia, ale w kategoriach składu. Generalnie nie próbuj naśladować C ++ w C, byłoby to bardzo trudne do odczytania i utrzymania.

Jeśli masz czas na naukę, zobacz, jak zbudowana jest aplikacja Ada, z jej obowiązkiem package(interfejs modułu) i package body(implementacją modułu).

To jest do kodowania.

W celu utrzymania (pamiętaj, że kodujesz raz, ale konserwujesz kilka razy) proponuję udokumentować swój kod; Doxygen to dla mnie fajny wybór. Proponuję również zbudowanie mocnego zestawu testów regresji, który umożliwia refaktoryzację.

mouviciel
źródło
30

Powszechnym błędem jest przekonanie, że techniki OO nie mogą być stosowane w C. Większość tak - po prostu są one nieco bardziej nieporęczne niż w językach ze składnią dedykowaną do tego zadania.

Jedną z podstaw solidnego projektu systemu jest hermetyzacja implementacji za interfejsem. FILE*i funkcje, że praca z nim ( fopen(), fread()etc.) jest przykładem na to, jak dobry enkapsulacji może być stosowany w C ustanowienie interfejsów. (Oczywiście, ponieważ C nie ma specyfikatorów dostępu, nie można wymusić, aby nikt nie zaglądał do wnętrza a struct FILE, ale zrobiłby to tylko masochista).

Jeśli to konieczne, zachowanie polimorficzne można uzyskać w C za pomocą tabel wskaźników funkcji. Tak, składnia jest brzydka, ale efekt jest taki sam jak funkcji wirtualnych:

j_random_hacker
źródło
11
Czytelny koder C zgubiłby się czytając ten kod ...
mouviciel
15
@mouviciel: Bzdury! Większość programistów C rozumie wskaźniki funkcji (a przynajmniej powinni), a poza tym nic się nie dzieje. Przynajmniej w systemie Windows sterowniki urządzeń i obiekty COM zapewniają swoją funkcjonalność w ten sposób.
j_random_hacker
8
Nie chodzi mi o niekompetencję, chodzi o niepotrzebne komplikacje. Wskaźniki funkcji są wspólne dla kodera C (np. Wywołania zwrotne), dziedziczenie nie jest. Wolę, aby koder C ++ w C wykorzystywał swój czas na naukę C, niż na tworzenie pseudo klas w C ++. To powiedziawszy, twoje podejście może być przydatne w niektórych przypadkach.
mouviciel
3
Właściwie możesz nawet symulować specyfikatory dostępu za pomocą idiomu C ++ pimpl. Jeśli prywatne elementy członkowskie typu są zamknięte w typie, który jest widoczny tylko w implementacji (czyli „plik .c”), użytkownicy interfejsu będą mieli trudności ze zmianą ich (oczywiście mogą zapisywać bzdury w pimpl, jeśli chcą cię celowo wkręcić , ale możesz zrobić to samo w C ++).
maska ​​bitów,
2
Downvoter: chcesz skomentować?
j_random_hacker,
15

Wszystkie dobre odpowiedzi.

Dodałbym tylko „zminimalizuj strukturę danych”. Może to być nawet łatwiejsze w C, ponieważ jeśli C ++ to „C z klasami”, OOP próbuje zachęcić cię do wzięcia każdego rzeczownika / czasownika w twojej głowie i przekształcenia go w klasę / metodę. To może być bardzo marnotrawne.

Na przykład załóżmy, że masz tablicę odczytów temperatury w punktach w czasie i chcesz wyświetlić je jako wykres liniowy w systemie Windows. Windows ma komunikat PAINT, a kiedy go otrzymasz, możesz zapętlić tablicę wykonując funkcje LineTo, skalując dane w miarę przechodzenia, aby przekonwertować je na współrzędne pikseli.

Zbyt wiele razy widziałem to, że ponieważ wykres składa się z punktów i linii, ludzie zbudują strukturę danych składającą się z obiektów punktowych i obiektów liniowych, z których każdy jest w stanie DrawMyself, a następnie uczynią to trwałym, opierając się na teorii, że jest w jakiś sposób „bardziej wydajne” lub że być może będą musieli umieć przesuwać kursor myszy nad częściami wykresu i wyświetlać dane numerycznie, więc wbudowują metody w obiekty, aby sobie z tym poradzić, i oczywiście polega na tworzeniu i usuwaniu jeszcze większej liczby obiektów.

W efekcie otrzymujesz ogromną ilość kodu, który jest bardzo czytelny i spędza tylko 90% czasu na zarządzaniu obiektami.

Wszystko to odbywa się w imię „dobrej praktyki programistycznej” i „wydajności”.

Przynajmniej w C prosty, skuteczny sposób będzie bardziej oczywisty, a pokusa budowania piramid słabsza.

Mike Dunlavey
źródło
1
Uwielbiam swoją odpowiedź i jest ona całkowicie prawdziwa. OOP musi umrzeć!
Jo So
@JoSo: Używam OOP, ale minimalnie.
Mike Dunlavey
Dla mnie „minimalnie” (chociaż nie wiem, co to dla ciebie oznacza) tak naprawdę nie liczy się jako OOP, czyli programowanie obiektowe - domyślnie obiekty.
Jo So
Używam też "OOP". Głównie dla moich modułów C, z których wiele ma procedury init () i exit ().
Jo So
11

Standardy kodowania GNU ewoluowały przez kilka dekad. Dobrze byłoby je przeczytać, nawet jeśli nie przestrzegasz ich co do joty. Myślenie o podniesionych w nich punktach daje ci solidniejszą podstawę do tego, jak skonstruować własny kod.


źródło
4
Nie każdemu się to podoba, z lxr.linux.no/linux+v2.6.29/Documentation/CodingStyle : „Po pierwsze, sugerowałbym wydrukowanie kopii standardów kodowania GNU, a NIE czytanie. Spalić je, to wielki gest symboliczny ”. Nie czytałem ich od wielu lat, ale Linus ma pewne uzasadnione zastrzeżenia.
hlovdal
1
@hlovdal: Oczywiście nie każdemu podoba się jeden konkretny standard kodowania, dlatego istnieje więcej niż jeden standard dla podobnych przypadków użycia. Ważną częścią jest to, abyś był spójny we własnych projektach, przestrzegał przynajmniej niektórych standardów, a nie de facto niespójności ad hoc.
JM Becker
4

Jeśli wiesz, jak uporządkować swój kod w Javie lub C ++, możesz przestrzegać tych samych zasad w przypadku kodu C. Jedyną różnicą jest to, że nie masz po swojej stronie kompilatora i musisz robić wszystko bardzo ostrożnie ręcznie.

Ponieważ nie ma pakietów i klas, musisz zacząć od uważnego zaprojektowania swoich modułów. Najpopularniejszym podejściem jest utworzenie oddzielnego folderu źródłowego dla każdego modułu. Aby rozróżnić kod między różnymi modułami, musisz polegać na konwencjach nazewnictwa. Na przykład poprzedz wszystkie funkcje nazwą modułu.

Nie możesz mieć klas w C, ale możesz łatwo zaimplementować „Abstrakcyjne typy danych”. Tworzysz plik .C i .H dla każdego abstrakcyjnego typu danych. Jeśli wolisz, możesz mieć dwa pliki nagłówkowe, jeden publiczny i jeden prywatny. Chodzi o to, że wszystkie struktury, stałe i funkcje, które mają zostać wyeksportowane, trafiają do publicznego pliku nagłówkowego.

Twoje narzędzia są również bardzo ważne. Przydatnym narzędziem dla C są kłaczki , które mogą pomóc Ci znaleźć nieprzyjemne zapachy w kodzie. Innym narzędziem, którego możesz użyć, jest Doxygen, które może pomóc w generowaniu dokumentacji .

kgiannakakis
źródło
4

Hermetyzacja jest zawsze kluczem do pomyślnego rozwoju, niezależnie od języka programowania.

Sztuczka, której użyłem, aby pomóc hermetyzować „prywatne” metody w C, polega na tym, że nie należy umieszczać ich prototypów w pliku „.h”.

Nate
źródło
3

Zasugerowałbym Ci sprawdzenie kodu dowolnego popularnego projektu C open source, takiego jak… hmm… jądro Linuksa lub Git; i zobacz, jak to zorganizują.

Ivan Krechetov
źródło
3

Reguła liczbowa dla złożonych zastosowań: powinna być łatwa do odczytania.

Aby uprościć złożone aplikacje, używam Divide and Conquer .

MrValdez
źródło
2

Sugerowałbym przeczytanie podręcznika C / C ++ jako pierwszy krok. Na przykład C Primer Plus jest dobrym odniesieniem. Przejrzenie przykładów da ci pomysł, jak zmapować twoją OO java na bardziej proceduralny język, taki jak C.

Tosa
źródło