<< powrót | Cz/B

Wytyczne Stylu Kodu

Style guide for FreeBSD

[Link] https://man.freebsd.org/cgi/man.cgi?query=style&sektion=9
/*
 * Bardzo ważne jednoliniowe komentarze wyglądają tak.
 */
/* Większość jednoliniowych komentarzy wygląda tak */
/*
 * Wieloliniowe komentarze wyglądają tak. Układaj normalne zdania. 
 * Wypełnij je żeby wyglądały jak prawdziwe akapity.
 */

Nagłówek z prawami autorskimi powinien być wieloliniowym komentarzem, w pierwszej linii z myślnikiem jako znakiem po gwiazdce:

/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 1984-2025 John Q. Public
 *
 * Long, boring license goes here, but trimmed for brevity
 */

Następnie pusta linia przed globalnymi plikami nagłówkowymi. Posortowane alfabetycznie:

#include    <conio.h>
#include    <stdio.h>

Kolejna pusta linia przed lokalnymi #include'ami.

#include    "pathnames.h"

Nie #define'uj ani nie deklaruj nazw w przestrzeni implementacji, z wyjątkiem interfejsów aplikacji.

Makra z efektami ubocznymi i definicjami stałych, są pisane dużymi literami. Jeśli są wieloliniowe wyrównaj backslashe następnej linii. Obejmij je do-while żeby można ich używać w warunku if.

#define MACRO(x,    y) do {                            \
       variable = (x) + (y);                           \
       (y) += 2;                                       \
} while (0)

Używaj albo spacji albo tabów. Wybierz raz i się tego trzymaj.

Koniec dużych #ifdef'ów możesz skomentować:

#ifdef COMPAT_43
/* Dużo linii kodu. */
#else /*    !COMPAT_43 */
/* I tutaj też. */
#endif /* COMPAT_43 */
#ifndef COMPAT_43
/* Dużo linii kodu. */
#else /*    COMPAT_43 */
/* I tutaj też. */
#endif /* !COMPAT_43 */

Używaj typów bez znaku "ISO C99" w formie uintXX_t

Używaj typu ISO C99 bool zamiast int czy boolean_t. Inkluduj <stdbool.h>

Preferowane snake_case zamiast camelCase czy PascalTitleCase.

Zmienne w strukturze: deklaruj posortowane po użyciu, potem po rozmiarze od największego, potem alfabetycznie. Każda ma osobną linię. Oddziel typ od nazwy tabulacjami, tak żeby były wyrównane do kolejnych linii. Nazwy po bardzo długim typie powinny być oddzielone pustą linią.

Główne struktury powinny być zdeklarowane na górze pliku, w którym są używane, lub w oddzielnym pliku nagłówkowym jeśli są używane w wielu plikach. Użycie struktury powinno być oddzielne od deklaracji, i extern'owe jeśli zdeklarowane w pliku nagłówkowym.


struct foo {
   struct foo      *next;          /* Lista aktywnych foo. */
   struct mumble   amumble;        /* Komentarz dla mumble. */
   int         bar;        /* Staraj się wyrównać komentarze */
   struct verylongtypename *baz;   /* Nie mieści się w dwócch tabach. */
};
struct foo *foohead;               /* Głowa globalnej listy foo. Osobno od deklaracji */

Unikaj typedef dla struktur.

Wszystkie funkcje powinny mieć gdzieś zdeklarowane prototypy.

Prywatne funkcje(używane tylko w jednym pliku) powinny być na górze pierwszego pliku źródłowego. Te funkcje lokalne powinny mieć deklarację static.

Funkcje używane gdzieś indziej powinny być zdeklarowane w pliku nagłówkowym.

Posortuj je logicznie, potem preferuj alfabetycznie.

Wyrównuj nazwy funkcji.

static char  *function(int _arg, const char *_arg2, struct foo *_arg3,
    struct bar *_arg4);
static void  usage(void);
/*
 * Komentuj co robią główne funkcje. Funkcja main powinna mówić co robi program
 */

Używaj getopt(3) do parsowania argumentów. Użycie argumentów posortowane alfabetycznie. Użyj komentarza /* FALLTHROUGH */ jeśli case spada do następnego case'a. Sprawdzaj poprawność argumentów numerycznych. Niedostępny kod skomentuj /* NOTREACHED */.

while ((ch = getopt(argc, argv, "abNn:")) != -1)
   switch (ch) {           /* Indent the switch. */
   case 'a':           /* Do not indent the case. */
       aflag = 1;      /* Indent case body one tab. */
       /* FALLTHROUGH */
   case 'b':
       bflag = 1;
       break;
   case 'N':
       Nflag = 1;
       break;
   case 'n':
       num = strtol(optarg, &ep, 10);
       if (num <= 0 || *ep != '\0') {
           warnx("illegal number, -n argument -- %s",
           optarg);
           usage();
       }
       break;
   case '?':
   default:
       usage();
   }
argc -= optind;
argv += optind;

Jedna spacja po słowach kluczowych(if, while, for, return, switch).

Nawiasy dla jednolinijkowych wyrażeń albo wszędzie, albo tylko tam gdzie potrzeba przejrzystości. Użycie w funkcji powinno być jedno i to samo.

Pętle bez warunku stopu powinny być for(;;) a nie while(1).

Niektóre części pętli for można opuścić.

for (; cnt < 15; cnt++) {
   stmt1;
   stmt2;
}

Pętla może deklarować i inicjalizować swój licznik.

for (int i = 0; i < 15; i++) {
   stmt1;
}

Wcięcia mają 8 znaków spacji(albo tab 8 znakowy).

Drugi poziom wcięcia ma 4 znaki. Długim wyrażeniom daj operator na końcu linii.

while (cnt < 20 && this_variable_name_is_too_long &&
    ep != NULL)
       z = a + really + long + statement + that + needs +
       two + lines + gets + indented + four + spaces +
       on + the + second + and + subsequent + lines;

Nie dodawaj białych znaków na końcu linii, używaj spacji po tabach. Nie używaj więcej spacji niż gdyby tab dodał, nie używaj spacji przed tabem.

Zamykający i otwierający nawias wąsaty jest w tej samej linii co else. Można pominąć niepotrzebne nawiasy.

if (test)
   stmt;
else if (bar) {
   stmt;
   stmt;
} else
   stmt;

Brak spacji po nazwie funkcji. Przecinki mają po sobie spację. Brak spacji po '(' lub '[' i brak spacji przed ')' i ']'.

error = function(a1, a2);
if (error != 0)
   exit(error);

Operatory jednoargumentowe nie potrzebują spacji, dwuargumentowe już tak.

Używaj nawiasów tylko jeśli wymaga tego priorytet operatorów lub jeśli wyrażenie jest zagmatwane. Pamiętaj że inni mogą łatwiej się pogubić niż ty.

Czy TY rozumiesz następujące?

a = b->c[0] + ~d == (e || f) || g && h ? i : j >> 1;
k = !(l & FLAGS);

Wyjście z programu powinno być 0 na sukcesie, 1 na porażce/błędzie.

exit(0);        /*
    * Odpuść oczywisty kometarz:
    * "Wyjdź z 0 na sukcesie."
    */

Typ zwracany funkcji powinien być w osobnej linii.

Nawias otwierający też.

static char *
function(int a1, int a2, float fl, int a4, struct bar *bar)
{

Deklaracje zmiennych sortuj po rozmiarze, potem alfabetycznie. Wiele zmiennych w linii jest ok. Jeśli linia jest zbyt długa, użyj ponownie typu w następnej.

Zmienne mogą być deklarowane i inicjalizowane, zwłaszcza stałe w zakresie.

Deklaracje mogą być w dowolnym bloku, ale muszą być zawsze przed wyrażeniami.

Unikaj inicjalizowania skomplikowanymi funkcjami w deklaracjach.

struct foo one, *two;
struct baz *three = bar_get_baz(bar);
double four;
int *five, six;
char *seven, eight, nine, ten, eleven, twelve;

four = my_complicated_function(a1, f1, a4);

Nie deklaruj funkcji w bloku funkcji. ANSI C mówi, że są widoczne w zakresie pliku, a nie bloku.

Rzutowanie i sizeof nie mają po sobie spacji. sizeof zawsze ma pojedyncze nawiasy.

NULL jest preferowaną stałą pustego wskaźnika. Nie używaj (type *)0 czy (type *)NULL gdzie kompilator zna typ, np: w przypisaniach.

Testuj wskaźniki na NULL'a

(p = f()) == NULL

a nie:

!(p = f())

Używaj ! tylko do typu bool, np. użyj:

if (*p == '\0')

a nie:

f (!*p)

Procedury zwracające void * nie powinny go rzutować na żaden typ wskaźnika.

Wartości w wyrażeniach zwracanych powinny być objęte w nawiasy.

Nie używaj deklaracji i definicji w stylu K&R, są stare i zabronione w C23.

Długa lista parametrów powinna być wcięta na cztery spacje.

Zmienna lista argumentów powinna wyglądać tak:

#include <stdarg.h>

void
vaf(const char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   STUFF;
   va_end(ap);
   /* No return needed for void functions. */
}

static void
usage(void)
{
/* Opcjonalna pusta linia. */

Opcjonalnie daj pustą linię na początku funkcji bez lokalnych zmiennych.

Nie wstawiaj pustej jeśli funkcja ma zmienne, daj pustą po deklaracjach, a przed pierwszym wyrażeniem.

Używaj printf zamiast fputs, puts, putchar.

Pomoc użycia(usage) programu powinna być w tej kolejności:

  1. Opcje bez operandów alfabetycznie w pojedynczych kwadratowych nawiasach ('[' i ']')
  2. Następnie opcje z operandami alfabetycznie, każda opcja i jej argumenty we własnej parze nawiasów.
  3. Wymagane argumenty (jeśli są) następne, w kolejności wymaganej w użyciu.
  4. W końcu opcjonalne, wszystkie w nawiasach.

Kreska pionowa `|` oddziela opcje "albo-albo" i opcje "lub", wewnątrz nawiasów.

"usage: f [-aDde] [-b b_arg] [-m m_arg] req1 req2 [opt1 [opt2]]\n"
"usage: f [-a | -b] [-c [-dEe] [-n number]]\n"

(void)fprintf(stderr, "usage: f [-ab]\n");
exit(1);

W manualu/dokumentacji(LOL, jasne) powinny być alfabetycznie.

Używaj opcji kompilatora -Wall i staraj się unikać ostrzeżeń.

Linux kernel coding style (pobieżnie)

[Link] https://www.kernel.org/doc/html/v6.13-rc1/process/coding-style.html

8 znaków wcięcia, 16 drugiego poziomu, itp - jeśli masz 4+ poziomy wcięcia to robisz coś źle.

Switch i case na tym samym poziomie wcięcia.

switch (suffix) {
case 'G':
case 'g':
        mem <<= 30;
        break;
case 'M':
case 'm':
        mem <<= 20;
        break;
case 'K':
case 'k':
        mem <<= 10;
        // fallthrough
default:
        break;
}

Każde wyrażenie w osobnej linii, nie rób:

if (condition) do_this;
  do_something_everytime;

Nie używaj przecinka ',' zamiast nawiasów:

if (condition)
    do_this(), do_that();

Zawsze używaj nawiasów dla wielu wyrażeń:

if (condition) {
    do_this();
    do_that();
}

Nie rób wielokrotnych przypisań w linii: a = b = 5;

Jak K&R przykazali nawias { jest na końcu linii, a } w nowej:

if (x is true) {
        we do y
}

Wyjątek funkcje:

int function(int x)
{
        body of function
}

Wyjątek do-while:

do {
        body of do-loop
} while (condition);

Wyjątek else:

if (x == y) {
        ..
} else if (x > y) {
        ...
} else {
        ....
}

Nie używaj nawiasów dla jednego wyrażenia:

if (condition)
    action();

oraz

if (condition)
    do_this();
else
    do_that();

Używaj nawiasów dla kilku wyrażeń w pętli:

while (condition) {
    if (test)
        do_something();
}

Zakaz: "master/slave" => "primary,main / secondary". Zakaz: "blacklist/whitelist" => "denylist/allowlist" "blocklist/passlist"

Odpuść sobie typedef.

Funkcje robią jedną rzecz. Mieszczą się w terminalu 80x24. Rozbij większe na inline pomocnicze funkcje. Max 5-10 zmiennych użytych.

Używaj goto z ładnymi labelkami. Nie jesteś na uniwersytecie, kto Ci zabroni.

Google C++ Style Guide (tylko to co dotyczy C)

[Link] https://google.github.io/styleguide/cppguide.html)

W plikach nagłówkowych używaj guard'ów, plik.h:

#ifndef __PLIK_H
#define __PLIK_H

#include...

#endif /* __PLIK_H */

Jak używasz czegoś w pliku źródłowym to include'uj nagłówek. Nie polegaj na reakcji łańcuchowej, że nagłówek include'uje ci nagłówki.

Unikaj prototypów bez definicji w plikach źródłowych:

// In a C++ source file:
class B;
void FuncInB();
extern int variable_in_b;
ABSL_DECLARE_FLAG(flag_in_b);

Używaj plików nagłówkowych.

// b.h:
struct B {};
struct D : B {};

// good_user.cc:
#include "b.h"
void f(B*);
void f(void*);
void test(D* x) { f(x); }  // Calls f(B*)

Unikaj inline, chyba że 10 lub mniej linijek.

Nazwy plików: małe znaki i podkreślnik:

Jeśli używasz #include do ładowania nie plików nagłówkowych(czemu to robisz?), to użyj rozszerzenia .inc zamiast .h

Reszta dotyczy typowo C++ albo jest sprzeczna z powyższymi dwoma.