Konwencje nazewnictwa stosowane dla zmiennych i funkcji w C [zamknięty]

13

Podczas kodowania dużego projektu w CI pojawił się problem. Jeśli będę nadal pisać więcej kodu, nadejdzie czas, kiedy będzie mi trudno go zorganizować. Mam na myśli, że nazewnictwo funkcji i zmiennych dla różnych części programu może wydawać się pomieszane.

Zastanawiałem się więc, czy istnieją użyteczne konwencje nazewnictwa, których można użyć dla zmiennych C i funkcji?

Większość języków sugeruje konwencję nazewnictwa. Ale dla C jedyną rzeczą, którą do tej pory przeczytałem, jest to, że nazwy powinny być opisowe dla czytelności kodu.

EDYTOWAĆ:

Przykłady niektórych przykładów sugerowanych konwencji nazewnictwa:

Czytałem gdzieś więcej konwencji nazewnictwa dla Java, ale nie pamiętałem gdzie.

Aseem Bansal
źródło
Podaj przykłady języków z sugerowanymi konwencjami nazewnictwa. I gdzie możemy znaleźć te konwencje nazewnictwa.
Philip
@Philip Dodano przykłady
Aseem Bansal
1
Nie powinno być problemu ze zmiennymi, ponieważ nie używasz globali. A dla nazw funkcji: jeśli nazwa modułu jest order.c, można wymienić funkcje order_add(), order_del()i takie. Mogą istnieć stare systemy, które mówią, że nazwa musi być unikalna w ciągu pierwszych 8 znaków. Kiedy później przypadkowo przełączysz się na c ++, pokochasz pisanie, order::add()a order::del()potem.
ott--

Odpowiedzi:

17

Jeśli będę nadal pisać więcej kodu, nadejdzie czas, kiedy będzie mi trudno go zorganizować.

To jest twój problem: popraw organizację, a styl powinien płynąć łatwiej.

Nie czekaj, aby uporządkować kod: utrzymuj swój kod uporządkowany na bieżąco. Chociaż język tego nie robi, kod powinien być nadal zorganizowany w moduły o niskim sprzężeniu i wysokiej spójności.

Moduły te następnie naturalnie zapewniają przestrzeń nazw. Skróć nazwę modułu (jeśli jest długi) i prefiks nazwy funkcji w module, aby uniknąć kolizji.

Na poziomie indywidualnych identyfikatorów są one z grubsza w porządku subiektywnym:

  1. wybierz konwencję i trzymaj się jej
    • np. function_like_this(struct TypeLikeThis variable)jest powszechne
  2. zdecydowanie unikać notacji węgierskiej (przepraszam JNL)

    • chyba że chcesz go używać zgodnie z pierwotnym przeznaczeniem, co oznacza notację aplikacji Simonyi zamiast strasznej wersji systemowej

      Dlaczego? Mógłbym napisać esej na ten temat, ale zamiast tego zasugeruję, abyś przeczytał ten artykuł Joela Spolsky'ego, a następnie polował na więcej, jeśli jesteś zainteresowany. Na dole znajduje się link do oryginalnej pracy Simonyi.

  3. unikaj typowania wskaźników, chyba że są to naprawdę nieprzejrzyste typy plików cookie - tylko mylą

    struct Type *ok;
    typedef struct Type *TypePtr;
    TypePtr yuck;

    Co rozumiem przez nieprzezroczysty typ pliku cookie ? Mam na myśli coś używanego wewnątrz modułu (lub biblioteki, czy cokolwiek innego), co musi zostać przekazane do kodu klienta, ale tego kodu klienta nie można użyć bezpośrednio. Po prostu przekazuje go z powrotem do biblioteki.

    Na przykład biblioteka bazy danych może ujawniać interfejs podobny do tego

    /* Lots of buffering, IPC and metadata magic held in here.
       No, you don't get to look inside. */
    struct DBContextT;
    /* In fact, you only ever get a pointer, so let's give it a nice name */
    typedef struct DBContexT *DBContext;
    
    DBContext db_allocate_context(/*maybe some optional flags?*/);
    void db_release_context(DBContext);
    int db_connect(DBContext, const char *connect);
    int db_disconnect(DBContext);
    int db_execute(DBContext, const char *sql);

    Teraz kontekst jest nieprzejrzysty dla kodu klienta, ponieważ nie można zajrzeć do środka. Po prostu przekaż go z powrotem do biblioteki. Coś podobnego FILEjest również nieprzezroczyste, a deskryptor pliku liczb całkowitych jest również plikiem cookie , ale nie jest nieprzejrzysty.


Uwaga dotycząca projektu

Użyłem powyższego wyrażenia niskie sprzężenie i wysoka kohezja bez wyjaśnienia, i czuję się z tym trochę źle. Możesz go wyszukać i prawdopodobnie znaleźć dobre wyniki, ale postaram się krótko rozwiązać (ponownie, mógłbym napisać esej, ale spróbuję tego nie robić).

Naszkicowana powyżej biblioteka DB pokazuje niskie sprzężenie, ponieważ odsłania mały interfejs dla świata zewnętrznego. Ukrywanie szczegółów implementacji (częściowo przy pomocy nieprzezroczystej sztuczki cookie) zapobiega uzależnieniu kodu klienta od tych szczegółów.

Wyobraź sobie, że zamiast nieprzezroczystego pliku cookie deklarujemy strukturę kontekstu, aby jego zawartość była widoczna i zawiera deskryptor pliku gniazda dla połączenia TCP z bazą danych. Jeśli następnie zmienimy implementację na obsługę segmentu pamięci współdzielonej, gdy baza danych działa na tym samym komputerze, klient musi zostać ponownie skompilowany, a nie tylko ponownie połączony. Co gorsza, klient mógł zacząć korzystać z deskryptora pliku, na przykład wywołując setsockoptzmianę domyślnego rozmiaru bufora, a teraz również potrzebuje zmiany kodu. Tam, gdzie jest to praktyczne, wszystkie te szczegóły powinny być ukryte wewnątrz naszego modułu, co zapewnia niskie sprzężenie między modułami.

Przykład pokazuje również wysoką spójność , ponieważ wszystkie metody w module dotyczą tego samego zadania (dostęp do bazy danych). Oznacza to, że tylko kod, który musi wiedzieć o szczegółach implementacji (tj. Zawartości naszego pliku cookie) faktycznie ma do nich dostęp, co upraszcza debugowanie.

Widać również, że posiadanie jednego problemu ułatwiło wybranie prefiksu do grupowania tych funkcji razem.

Powiedzenie, że ten przykład jest dobry, jest łatwe (zwłaszcza, że ​​nie jest nawet kompletne), ale nie pomaga natychmiast. Sztuczka polega na tym, aby podczas pisania i rozszerzania kodu obserwować funkcje, które robią podobne rzeczy lub działają na tych samych typach (które mogą być kandydatami na własny moduł), a także funkcje, które wykonują wiele różnych rzeczy, które nie są naprawdę powiązane i mogą być kandydatami do podziału.

Bezużyteczny
źródło
Czy możesz mi pomóc zrozumieć, dlaczego unika się Węgier? Po prostu ciekawy, aby dowiedzieć się więcej na ten temat. :)
JNL
@JNL: Komentarz jest zbyt krótki, aby poprawnie wyjaśnić. Sugeruję opublikowanie go jako nowego pytania.
Bart van Ingen Schenau
with low coupling and high cohesion. Co to znaczy? I proszę wyjaśnić nieprzejrzyste typy plików cookie. Nie mam pojęcia co to znaczy.
Aseem Bansal
Próbowałem zająć się zarówno krótko, jak i szczerze mówiąc, zawiodłem zwięźle. Mam nadzieję, że powinno to jednak zacząć.
Bezużyteczne
Odpowiadam po kilku dniach. Przepraszam za to. Przeczytałem twój opis low coupling and high cohesion. Oznacza to więc przede wszystkim enkapsulowanie rzeczy, kiedy mogę, i powinno się to odbywać w taki sposób, aby funkcje, które faktycznie potrzebują, miały dostęp. Niektóre rzeczy przeszły mi przez głowę, ale myślę, że rozumiem.
Aseem Bansal,
5

Moim zdaniem 90% problemu z nazewnictwem zostanie rozwiązane, jeśli weźmiesz pod uwagę trzy rzeczy: a) ustaw nazwy zmiennych i funkcji tak opisowo, jak to możliwe, b) spójne w całym kodzie (tj. Jeśli funkcja nazywa się addNumbers, a druga funkcja powinna mieć nazwę multiplyNumbers, a nie numbersMul) ic) staraj się skracać nazwy, jeśli to możliwe, ponieważ musimy je wpisać.

Biorąc to pod uwagę, jeśli chcesz spojrzeć na inne aspekty tego tematu, strona Wikipedia na temat konwencji nazewnictwa ma dobrą listę rzeczy, o których powinieneś pamiętać. Ma także sekcję dotyczącą C i C ++:

W C i C ++ słowa kluczowe i standardowe identyfikatory bibliotek są przeważnie pisane małymi literami. W standardowej bibliotece C najczęściej używane są skrócone nazwy (np. Isalnum dla funkcji sprawdzającej, czy znak jest alfanumeryczny), podczas gdy standardowa biblioteka C ++ często używa podkreślnika jako separatora słów (np. Out_of_range). Identyfikatory reprezentujące makra są, zgodnie z konwencją, pisane tylko dużymi literami i znakami podkreślenia (jest to związane z konwencją w wielu językach programowania polegającą na stosowaniu wielkich liter dla stałych). Nazwy zawierające podwójny znak podkreślenia lub zaczynające się znakiem podkreślenia i wielką literą są zarezerwowane do implementacji (kompilator, biblioteka standardowa) i nie powinny być używane (np. Zastrzeżone__ lub _Reserved). [5] [6] Jest to pozornie podobne do stropowania, ale semantyka różni się:

Daniel Scocco
źródło
3
„staraj się, aby nazwy były krótkie, jeśli to możliwe”. Użyj IDE z autouzupełnianiem, wtedy nazwy funkcji mogą być tak długie i opisowe, jak trzeba, wystarczy wpisać tylko raz.
Joel
1
@Joel straszna rada. Nie wszyscy będą używać tego samego IDE co Ty.
James
6
@James Nie muszą, mogą po prostu użyć dowolnego przyzwoitego IDE. Nie musisz poświęcać przejrzystości dla wydajności.
Joel
Termin IDE jest teraz trochę cienki od kilku dni. Technicznie Notepad ++ to IDE, ponieważ można go skonfigurować do kompilacji i uruchomienia projektu, ale przede wszystkim jest to edytor tekstowy. I automatycznie się kończy.
Philip
5

Jedynym trudnym ograniczeniem w C jest brak przestrzeni nazw. Dlatego musisz znaleźć sposób, aby odróżnić rename()funkcję biblioteki systemu plików od rename()funkcji biblioteki multimediów . Zwykłym rozwiązaniem jest przedrostek, taki jak: filesystem_rename()i media_rename().

Inne ogólne porady to: bądź konsekwentny w ramach projektu lub zespołu. Czytelność zostanie poprawiona.

mouviciel
źródło
+1: Dotyczy to szczególnie eksportowanych symboli w bibliotece. „Przykro mi, ale ta biblioteka systemu plików nie pasuje do tej biblioteki multimediów, ponieważ obie mają zmienioną nazwę funkcji wyeksportowanej.
Residuum
2

JEŚLI SZUKASZ GLOBALNIE ZAAKCEPTOWANEGO FORMATU

MISRA / JSF / AUTOSAR obejmuje prawie 100% wszystkich standardów branżowych dotyczących nazewnictwa i organizowania kodu C / C ++. Problem polega na tym, że nie będą one dostępne za darmo, tzn. Każdy przewodnik kosztuje trochę pieniędzy. Wiem, że standardowa książka MISRA 2008 C / C ++ kosztuje prawdopodobnie około 50 USD.

Możesz myśleć o nich jak o Harvard Referencing dla bibliografii i dodatkowej lektury podczas pisania dziennika. Użyłem MISRY i jest to dobry sposób na nazwanie funkcji i zmiennych oraz uporządkowanie ich w celu właściwego wykorzystania.

JEŻELI SZUKASZ CZASU TYMCZASOWEGO

Podane przez ciebie odniesienia do Pythona i Javy są w porządku. Widziałem ludzi stosujących komentowanie, nazewnictwo i organizowanie kodu w stylu javadoc. W rzeczywistości w moim ostatnim projekcie musiałem napisać kod C ++ w funkcjach / nazwach podobnych do języka Java. Dwa powody tego:

1) Najwyraźniej było to łatwiejsze do naśladowania.

2) Wymagania dotyczące kodu produkcyjnego nie dotknęły podstaw krytycznych dla bezpieczeństwa standardów oprogramowania systemowego.

3) Stary kod był (jakoś) w tym formacie.

4) Doxygen pozwolił komentować Javadoc sytle. W tym momencie używaliśmy doxygen do generowania dokumentacji dla pracowników produkcji.

Wielu programistów będzie temu przeciwnych, ale osobiście uważam, że nie ma nic złego w przyjmowaniu funkcji / zmiennych w stylu javadoc w C / C ++. TAK OCZYWIŚCIE, niezależnie od tego należy zająć się praktykami organizacji kontroli przepływu, bezpieczeństwa wątków itp. Jednak nie jestem tutaj wnioskodawcą. Nie wiem też, jak rygorystyczne są wymagania dotyczące formatu kodu produkcyjnego. Nie przekierowując go do tematu nie na temat, proponuję przejrzeć swoje wymagania, dowiedzieć się, jak jesteś uzależniony od konkretnej konwencji nazewnictwa i wybrać rozwiązanie wspomniane w moich i odpowiedziach innych

Mam nadzieję, że to pomogło !?

hagubear
źródło
Właściwie to prosiłem o osobiste kody C. Ale zapamiętam twoją sugestię.
Aseem Bansal,
@AseemBansal Osobiste lub profesjonalne, te są dobre do nauczenia, a także dobrze umieścić na swoim CV :) .... Do ciebie.
hagubear
0

Byłoby kilka ważnych rzeczy, które należy wziąć pod uwagę podczas nazywania;

  1. Spójrz na typ actionObject lub ObjectAction. (Obiekt nie dla C. Ale ogólnie, gdy idziesz do innych języków zorientowanych obiektowo) To powinno pomóc

  2. Reszta byłaby ZGODNA, krótka i opisowa na pewno.

  3. Ponadto, należy mieć wyłączny cel każdej zdefiniowanej zmiennej i funkcji, np .: Jeśli ma ona tymczasowo przechowywać wartość, nazwij ją jako nTempVal dla int
  4. Zmienne powinny być rzeczownikiem, a Metody powinny być czasownikiem.
JNL
źródło
6
Notacja węgierska (poprzedzająca zmienną literami oznaczającymi typ) nie prowadzi do końca bólu. Na szczęście w większości wyszedł z mody.
Gort the Robot
@StevenBurnap Byłem ciekawy, dlaczego uniknięto formatu węgierskiego? Wierzę, że tego nauczyli nas w szkole i widziałem taki kod także w niektórych miejscach pracy. Który z nich poleciłbyś, gdyby nie węgierski. Dzięki
JNL
1
Najlepsza konwencja nazewnictwa to tylko jedna konsekwentnie stosowana, z jasnymi, opisowymi nazwami idealnie utrzymywanymi stosunkowo krótko, bez nadmiernego skrótu i ​​unikania zbędnych prefiksów. Notacja węgierska ma niewielką rzeczywistą użyteczność, utrudnia czytanie kodu i utrudnia zmianę typów.
Gort the Robot
2
Oto opis pierwotnej intencji i obrzydliwości, jaką stała się notacja węgierska: joelonsoftware.com/articles/Wrong.html
Residuum
@Residuum To był dobry link. Bardzo mi pomógł. Doceniam to.
JNL
0

Większość odpowiedzi jest dobra, ale chcę powiedzieć kilka rzeczy na temat konwencji nazewnictwa dla bibliotek i dołączonych plików, podobnie jak przy użyciu przestrzeni nazw w innych językach, takich jak C ++ lub Java:

Jeśli budujesz bibliotekę, znajdź wspólny przedrostek dla eksportowanych symboli, tj. Funkcje globalne, typy typef i zmienne. Zapobiegnie to konfliktom z innymi bibliotekami i zidentyfikuje funkcje jako pochodzące od twoich. To jest trochę aplikacji Notacje węgierskie.

Może pójdź jeszcze dalej i pogrupuj wyeksportowane symbole: libcurl używa curl_ * dla symboli globalnych, curl_easy_ *, curl_multi_ * i curl_share_ * dla różnych interfejsów. Oprócz używania curl_ * dla wszystkich funkcji, dodali kolejny poziom „przestrzeni nazw” dla różnych interfejsów: wywoływanie funkcji curl_easy_ * na uchwycie curl_multi_ * wygląda teraz źle, zobacz nazwy funkcji na http: // curl. haxx.se/libcurl/c/

Zachowując reguły dla eksportowanych symboli, powinieneś ich używać do funkcji statycznych w #includeplikach ed: Spróbuj znaleźć wspólny prefiks dla tych funkcji. Może masz funkcje narzędzia ciąg statyczny w pliku o nazwie „my_string”? Poprzedź wszystkie te funkcje my_string_ *.

Pozostałość
źródło
Przez wyeksportowane symbole rozumiesz zmienne globalne, funkcje, typedefs itp., Jeśli mam rację. Czy możesz wyjaśnić trochę na temat grupowania eksportowanych symboli? Myślałem, że wyjaśniłeś to już w poprzednim akapicie. Co dodałeś w trzecim akapicie?
Aseem Bansal