Jakie są najczęstsze konwencje nazewnictwa w C?

126

Jakie konwencje nazewnictwa są powszechnie używane w języku C? Wiem, że są co najmniej dwa:

  1. GNU / linux / K&R z funkcją lower_case_functions
  2. ? imię ? z funkcjami UpperCaseFoo

Mowa tutaj tylko o C. Większość naszych projektów to małe systemy wbudowane, w których używamy C.

Oto ten, którego zamierzam użyć w następnym projekcie:


C Naming Convention

Struct              TitleCase
Struct Members      lower_case or lowerCase

Enum                ETitleCase
Enum Members        ALL_CAPS or lowerCase

Public functions    pfx_TitleCase (pfx = two or three letter module prefix)
Private functions   TitleCase
Trivial variables   i,x,n,f etc...
Local variables     lower_case or lowerCase
Global variables    g_lowerCase or g_lower_case (searchable by g_ prefix)
JeffV
źródło
7
Nie wymuszałbym prefiksu „g_” na zmiennych globalnych; Wymuszałbym znaczące nazwy (więc client_locale, a nie cl_lc jako globalna nazwa zmiennej). Klasyczny C nie używa wielbłąda; Napisałem kod w przypadku wielbłąda w C i wygląda to dziwnie (więc już tak tego nie robię). To powiedziawszy, nie jest źle - a konsekwencja jest ważniejsza niż stosowana konwencja. Unikaj czcionek typu, które hermetyzują wskaźniki struktur; weź pod uwagę standard C - „FILE *” jest zapisywane w ten sposób, a nie FILE_PTR.
Jonathan Leffler
2
@Jonathan Leffler, co jest nie tak z g_ w oznaczaniu globalnych? W systemach wbudowanych miałem wcześniej problemy, w których trudno było wyśledzić zależności międzymodułowe za pomocą zmiennych globalnych i extern g_somevar. Osobiście uważam, że generalnie jest to zły pomysł, ale tego typu rzeczy zwykle robi się ze względu na wydajność. Na przykład globalna flaga ustawiana przez przerwanie wskazujące, że dane są gotowe.
JeffV
2
Co jest warte, ta konwencja nazewnictwa została w większości wyrwana z konwencji PalmOS API. Jest również podobna do konwencji używanej w książce O'Reilly: „Programowanie systemów wbudowanych w C i narzędziach programistycznych GNU”. Osobiście podoba mi się TitleCase w nazwach funkcji. Myślałem o korzystaniu z lowerCamelCase w wewnętrznych funkcjach łączących (które nazwałem prywatnymi w moim pytaniu).
JeffV
3
@Chris Lutz, zgadzam się z całego serca. Wszędzie tam, gdzie to możliwe, vars należy utrzymywać w jak najwęższym zakresie. Zauważ, że w rzeczywistości omawiamy trzy zakresy: lokalny dla funkcji, lokalny dla modułu (brak powiązania zewnętrznego ze zmienną) i globalne z połączeniem zewnętrznym. W systemach wbudowanych często występują zmienne „globalne dla modułu”. Dlatego należy uważać, aby zidentyfikować elementy globalne z połączeniami zewnętrznymi, aby można je było ograniczyć do minimum i zrozumieć interakcje modułów. W tym miejscu przydatny jest prefiks „g_”.
JeffV
47
Lubię poprzedzać zmienne globalne znakiem //.
plafer

Odpowiedzi:

129

Najważniejsza jest tutaj konsekwencja. To powiedziawszy, kieruję się konwencją kodowania GTK +, którą można podsumować w następujący sposób:

  1. Wszystkie makra i stałe w wielkich literach: MAX_BUFFER_SIZE, TRACKING_ID_PREFIX.
  2. Nazwy struktur i typy w camelcase: GtkWidget, TrackingOrder.
  3. Funkcje działające na strukturach: klasyczny styl C: gtk_widget_show(), tracking_order_process().
  4. Wskaźniki: nic nadzwyczajnego: GtkWidget *foo, TrackingOrder *bar.
  5. Zmienne globalne: po prostu nie używaj zmiennych globalnych. Oni są źli.
  6. Funkcje, które istnieją, ale nie powinny być wywoływane bezpośrednio, lub mają niejasne zastosowania, lub cokolwiek innego: jedno lub więcej podkreślenia na początku: _refrobnicate_data_tables(), _destroy_cache().
axel_c
źródło
13
W punkcie szóstym wolę używać statici pomijać przedrostek modułu, więc gdyby gtk_widget_show()była funkcją z zakresem plikowym, stałaby się po prostu widget_show()dodaną statyczną klasą pamięci.
August Karlstrom
27
Dodatkowa uwaga do punktu 6: standard C zawiera pewne zasady dotyczące rezerwowania nazw, które zaczynają się od, w _celu wdrożenia i przyszłego wykorzystania. Istnieje kilka wyjątków od nazw zaczynających się od, _ale moim zdaniem nie warto ich zapamiętywać. Bezpieczną zasadą jest nigdy nie używać _w kodzie nazw zaczynających się od . Odpowiedni wpis FAQ C: c-faq.com/~scs/cgi-bin/faqcat.cgi?sec=decl#namespace
jw013
5
# 2 to dokładniej przypadek górnego wielbłąda lub paskal . Wielbłądzie lub małe litery wielbłąda używają małych liter na pierwszej literze.
Clint Pachl
9
A co z lokalnymi zmiennymi wielowyrazowymi? my_var czy myVar?
Dean Gurvitz
5
Global variables: just don't use global variables. They are evil.- chyba że pracujesz nad wbudowanym projektem i masz 1024 bajty pamięci RAM i procesor 8 MHz.
Kamil
30

„Wskaźniki struktury” nie są bytami, które wymagają klauzuli konwencji nazewnictwa, aby je objąć. Po prostu struct WhatEver *. NIE ukrywaj faktu, że istnieje wskaźnik związany ze sprytną i „oczywistą” czcionką. Nie służy to celowi, jest już do pisania i niszczy równowagę między deklaracją a dostępem.

rozwijać
źródło
29
+1 dla rzeczy „nie ukrywaj wskaźników” - nawet jeśli ta odpowiedź nie dotyczy większości reszty pytania (jeszcze).
Jonathan Leffler
1
@unwind, raczej się zgadzam. Jednak czasami wskaźnik nie jest przeznaczony do zewnętrznego wyłuskiwania i jest bardziej uchwytem dla konsumenta niż faktycznym wskaźnikiem do struktury, której będzie używał. To jest to, dla czego zostawiłem TitleCasePtr. typedef struct {pola} MyStruct, * MyStructPtr;
JeffV
Usuwam TitleCasePtr, to odwraca uwagę od rzeczywistego pytania.
JeffV
1
-1 ode mnie, ponieważ deklaracja typu wskaźnika zmniejsza bałagan, szczególnie w sygnaturach funkcji, a "nierównowaga" między deklaracją a dostępem pojawia się tylko w pliku implementacji - klient nie powinien (nie powinien) uzyskiwać bezpośredniego dostępu do elementów członkowskich pola.
August Karlstrom
1
@AugustKarlstrom Fine. Nie rozumiem jednak, co jest „tylko” w pliku implementacji, czy nie jest to też kod? Nie zinterpretowałem pytania jako tylko o „zewnętrzne” nazwy. Cały kod jest „implementacją” na pewnym poziomie.
odpoczynek
17

Po pierwsze, C nie ma funkcji publicznych / prywatnych / wirtualnych. To jest C ++ i ma różne konwencje. W C zazwyczaj masz:

  • Stałe w ALL_CAPS
  • Podkreślenia do oddzielania słów w strukturach lub nazwach funkcji, prawie nigdy nie widzisz przypadku wielbłąda w C;
  • structs, typedefs, unions, Members (of unions and structs) i wartości wyliczenia zazwyczaj są pisane małymi literami (z mojego doświadczenia), a nie w konwencji C ++ / Java / C # / etc, w której pierwsza litera jest wielką, ale myślę, że jest to możliwe w C też.

C ++ jest bardziej złożony. Widziałem tutaj prawdziwą mieszankę. Wielbłądowe litery dla nazw klas lub małe litery + podkreślenia (z mojego doświadczenia wynika, że ​​wielbłądzie są bardziej powszechne). Struktury są używane rzadko (i zazwyczaj dlatego, że wymaga ich biblioteka, w przeciwnym razie używałbyś klas).

cletus
źródło
@cletus, zdaję sobie z tego sprawę. Przez prywatne rozumiem funkcje, które nie są ujawniane zewnętrznie w nagłówku modułu i nie są przeznaczone do użycia przez kod zewnętrzny w stosunku do modułu. Publiczne byłyby funkcjami API modułu przeznaczonymi do użytku zewnętrznego.
JeffV
3
Możesz traktować funkcje statyczne jako prywatne; pytanie nie wspomina o wirtualności. Ale +1 za „rzadko widzę skrzynkę wielbłąda w C”.
Jonathan Leffler
2
Myślę, że Jeff miał external linkagena myśli „funkcje publiczne” i internal linkage„funkcje prywatne”.
pmg
1
Widziałem stałe zaczynające się od ak oraz w: kBufferSize. Nie jestem pewien, skąd to pochodzi.
JeffV
2
ALL_CAPSjest często używany także dla wartości wyliczeniowych.
kawiarnia
15

Wiesz, lubię to, aby było to proste, ale jasne ... Więc oto, czego używam, w C:

  • Zmienne trywialne : i,n,citd. (Tylko jedna litera. Jeśli jedna litera nie jest jasna, zmień ją jako zmienną lokalną)
  • Zmienne lokalne :lowerCamelCase
  • Zmienne globalne :g_lowerCamelCase
  • Zmienne Const :ALL_CAPS
  • Zmienne wskaźnika : dodaj p_do przedrostka. Dla zmiennych globalnych byłoby to gp_vardla zmiennych lokalnych p_var, dla zmiennych stałych p_VAR. Jeśli używane są dalekie wskaźniki, użyj fp_zamiast p_.
  • Struktury : ModuleCamelCase(Moduł = pełna nazwa modułu lub 2-3-literowy skrót, ale nadal w formacie CamelCase.)
  • Zmienne składowe struktury :lowerCamelCase
  • Wyliczenia :ModuleCamelCase
  • Wartości wyliczenia :ALL_CAPS
  • Funkcje publiczne :ModuleCamelCase
  • Funkcje prywatne :CamelCase
  • Makra :CamelCase

Wpisałem moje structs, ale używam tej samej nazwy zarówno dla tagu, jak i dla typedef. Tag nie jest przeznaczony do powszechnego użytku. Zamiast tego lepiej jest użyć typedef. Przekazuję również deklarację typedef w nagłówku modułu publicznego do hermetyzacji, aby móc użyć nazwy typedef'd w definicji.

Pełny struct przykład :

typdef struct TheName TheName;
struct TheName{
    int var;
    TheName *p_link;
};
SeanRamey
źródło
Nie wiem nic o frameworku qt, ale możesz napisać swój kod w dowolnym formacie stylu. O ile wiem, nic cię przed tym nie powstrzymuje.
SeanRamey,
10

Programując jednocześnie w językach C #, java, C, C ++ i obiektywnym C przyjąłem bardzo prostą i przejrzystą konwencję nazewnictwa, aby uprościć sobie życie.

Przede wszystkim opiera się na sile nowoczesnych IDE (takich jak eclipse, Xcode ...), z możliwością uzyskania szybkich informacji przez najechanie kursorem lub kliknięcie ctrl ... Akceptując to, zrezygnowałem z użycia dowolnego prefiksu, sufiksu i inne znaczniki, które są po prostu podawane przez IDE.

Następnie konwencja:

  • Wszelkie imiona MUSZĄ być czytelnym zdaniem wyjaśniającym, co masz. Na przykład „to moja konwencja”.
  • Następnie 4 metody wyciągnięcia konwencji z zdania:
    1. THIS_IS_MY_CONVENTION dla makr, członków wyliczenia
    2. ThisIsMyConvention dla nazwy pliku, nazwy obiektu (klasa, struktura, wyliczenie, unia ...), nazwa funkcji, nazwa metody, typedef
    3. this_is_my_convention zmienne globalne i lokalne,
      parametry, elementy struktury i unii
    4. thisismyconvention [opcjonalne] zmienne bardzo lokalne i tymczasowe (takie jak indeks pętli for ())

I to wszystko.

To daje

class MyClass {
    enum TheEnumeration {
        FIRST_ELEMENT,
        SECOND_ELEMENT,
    }

    int class_variable;

    int MyMethod(int first_param, int second_parameter) {
        int local_variable;
        TheEnumeration local_enum;
        for(int myindex=0, myindex<class_variable, myindex++) {
             localEnum = FIRST_ELEMENT;
        }
    }
}
Luc Fourestier
źródło
8

Odradzałbym mieszanie obudowy wielbłąda i separacji podkreślenia (tak jak zaproponowałeś dla członków struktury). To jest mylące. Można by pomyśleć, hej mam, get_lengthwięc prawdopodobnie powinienem to zrobić, make_subseta potem dowiadujesz się, że tak jest makeSubset. Kieruj się zasadą najmniejszego zdziwienia i bądź konsekwentny.

Uważam, że CamelCase jest przydatne do wpisywania nazw, takich jak struktury, typedef i wyliczenia. Ale to wszystko. Dla całej reszty (nazwy funkcji, nazwy składowe struktury itp.) Używam underscore_separation.

Eli Bendersky
źródło
1
Tak, najważniejsze w każdej konwencji nazewnictwa jest przewidywalność i spójność. Ponadto, ponieważ sama biblioteka C używa małych liter z _ jako odstępów, zalecałbym używanie tego tylko po to, abyś nie musiał zajmować się 2 różnymi konwencjami nazewnictwa w projekcie (zakładając, że nie napiszesz opakowania wokół libc, aby pasuje do twojego nazewnictwa ... ale to jest obrzydliwe)
Earlz,
Używa również typów czcionek z „ t” na końcu, ale nie widzę nikogo, kto by to zalecał. W rzeczywistości standardowa biblioteka jest nawet niespójna: div_t (stdlib.h) jest strukturą, podobnie jak tm (time.h). Spójrz także na elementy składowe tm struct, wszystkie mają przedrostek tm, co wydaje się bezcelowe i brzydkie (IMO).
JeffV
1
„Uważam, że CamelCase jest przydatne do wpisywania nazw ...” Jeśli zaczniesz od wielkiej litery, w rzeczywistości jest to PascalCase.
Tagc
7

Oto (najwyraźniej) nietypowy, który okazał się przydatny: nazwa modułu w CamelCase, następnie podkreślenie, a następnie nazwa funkcji lub zakresu pliku w CamelCase. Na przykład:

Bluetooth_Init()
CommsHub_Update()
Serial_TxBuffer[]
Steve Melnikoff
źródło
2
Niezbyt niezwykłe, ale bardzo przydatne.
chux - Przywróć Monikę
3

Jedno mnie zdezorientowało: planujesz stworzyć nową konwencję nazewnictwa dla nowego projektu. Ogólnie powinieneś mieć konwencję nazewnictwa obejmującą całą firmę lub zespół. Jeśli masz już projekty, które mają jakąkolwiek formę konwencji nazewnictwa, nie powinieneś zmieniać konwencji dla nowego projektu. Jeśli powyższa konwencja jest tylko kodyfikacją twoich istniejących praktyk, to jesteś złoty. Im bardziej różni się on od istniejących de facto standardów, tym trudniej będzie nabrać ducha myślenia w nowym standardzie.

Jedyną sugestią, jaką chciałbym dodać, jest to, że polubiłem _t na końcu typów w stylu uint32_t i size_t. Jest to dla mnie bardzo C-ish, chociaż niektórzy mogą narzekać, że to po prostu „odwrócony” węgierski.

jmucchiello
źródło
3
Cóż, konwencje tutaj są wszędzie i są niespójne, dlatego zamierzam je udokumentować. Dlatego też pytam. Aby zobaczyć, jaki jest konsensus społeczności.
JeffV
Rozumiem ten ból. Ale musi istnieć podzbiór istniejących konwencji, który jest najbardziej popularny. Powinieneś zacząć tam, a nie na przypadkowej stronie internetowej. Powinieneś również zapytać innych programistów, co uważają za dobre.
jmucchiello
7
Uważam, że nazwy typów kończące się na _t są zastrzeżone przez standard POSIX.
kawiarnia
4
Nazwy kończące się na _t są zastrzeżone. Zobacz gnu.org/software/libc/manual/html_node/Reserved-Names.html , „Nazwy kończące się na„ _t ”są zarezerwowane dla dodatkowych nazw typów.”
Étienne
2

Powinieneś także pomyśleć o kolejności słów, aby ułatwić automatyczne uzupełnianie nazw .

Dobra praktyka: nazwa biblioteki + nazwa modułu + akcja + temat

Jeśli część nie jest istotna, pomiń ją, ale przynajmniej nazwa modułu i akcja powinna być zawsze przedstawiana.

Przykłady:

  • Nazwa funkcji: os_task_set_prio, list_get_size,avg_get
  • zdefiniować (tutaj zwykle nie ma części akcji ):OS_TASK_PRIO_MAX
Gábor Kiss-Vámosi
źródło
0

Może być wiele, głównie IDE, które dyktują pewne trendy, a konwencje C ++ również są forsowane. Dla C zwykle:

  • UNDERSCORED_UPPER_CASE (definicje makr, stałe, elementy członkowskie wyliczenia)
  • underscored_lower_case (zmienne, funkcje)
  • CamelCase (typy niestandardowe: struktury, wyliczenia, związki)
  • uncappedCamelCase (styl oppa Java)
  • UnderScored_CamelCase (zmienne, funkcje w rodzajach przestrzeni nazw)

Notacja węgierska dla globals jest dobra, ale nie dla typów. Nawet w przypadku trywialnych nazw użyj co najmniej dwóch znaków.

renonsz
źródło
-1

Myślę, że te mogą pomóc początkującym: Konwencja nazewnictwa zmiennych w c

  1. Musisz użyć alfabetu (az, AZ), cyfry (0-9) i poniżej wyniku (_). Nie pozwala na użycie żadnych znaków specjalnych, takich jak:%, $, #, @ itd. Możesz więc użyć user_name jako zmiennej, ale nie możesz użyć user & name .
  2. Nie można używać spacji między słowami. Możesz więc użyć nazwy użytkownika lub nazwy użytkownika lub nazwy użytkownika jako zmiennej, ale nie możesz użyć nazwy użytkownika .
  3. Nie można rozpocząć nazywania przy pomocy cyfry. Możesz więc użyć user1 lub user2 jako zmiennej, ale nie możesz jej używać 1user .
  4. Jest to język uwzględniający wielkość liter. Duże i małe litery są znaczące. Jeśli używasz zmiennej, takiej jak nazwa użytkownika , nie możesz używać NAZWA UŻYTKOWNIKA ani Nazwa użytkownika do użytku przez ojca.
  5. Nie możesz użyć żadnego słowa kluczowego (char, int, if, for, while itp.) Do deklaracji zmiennej.
  6. Standard ANSI rozpoznaje długość 31 znaków w nazwie zmiennej
Amranur Rahman
źródło
Ten post wyraźnie ma na celu omówienie konwencji nazewnictwa , a nie ograniczeń . Podałeś rzeczy, których nawet nie możesz zrobić, ponieważ są to błędy składniowe.
Chase