Строгих требований к расстановке отступов нет. Какой стиль хотите использовать - тот и используйте. Но нужно придерживаться следующих правил:
- Код не должен выглядеть страшно.
- Ширина кода вашей программы не должна превышать 76 символов. Изначально это требование было продиктовано особенностями текстовых терминалов (80х25 минус рамочки редактора), но и в современной жизни, даже на больших экранах, часто бывает нужно расположить два текста сравнения, либо перегружать экран вспомогательнымии окнами (отладчики etc.).
- Отступы должны быть кратны 2-м пробелам.
- Нельзя смешивать отступы пробелами и символы табуляции. Используйте что-то одно.
На языке Си, в отличии от С++ или Java, принято выносить отделение возвращаемых типов функций и их модификаторов от названия на отдельную строку. Например, так:
static void
my_function(int arg1, char arg2, void *arg3)
{
/* function body */
}
Во-первых, типы возвращаемых значений бывают достаточно длинными (вроде struct имя_структуры
), да ещё и модификаторы на некоторых платформах встречаются вида extern __declspec(dllexport)
. В общем, тут трудно уложиться в положенные 76 символов.
Во-вторых, это удобно при поиске функций по регулярному выражению ^имя_функции
, где символ ^
означает начало строки.
Для знаковых целочисленных типов данных используются короткие имена: char
(8 бит), int
(как правило, 32 бит). Всё. Никаких long long
или short
использовать не нужно. Есть замечательный заголовочный файл <stdint.h>
, в котором перечислены типы данных с фиксированной точностью: int8_t
, int16_t
, int32_t
, int64_t
. Имена char
и int
являются общераспространенными синонимами для int8_t
и int32_t
, поэтому их использование не возбраняется за исключением особо экзотических случаев.
А вот использование ключевого слова long
- строго запрещено, поскольку на разных платформах и компиляторах его размер разный.
Для беззнаковых типов данных нужно использовать только имена из <stdint.h>
: uint8_t
, uint16_t
, uint32_t
и uint64_t
.
Обратите внимание, что char
- это синоним понятия "байт", а вовсе не символ.
Использование #define
для объявления констант - строго запрещено!
Целочисленные константы нужно объявлять как перечисления:
enum {
CONST_VALUE_1 = 1,
CONST_VALUE_2 = 567,
CONST_VALUE_3 = 890
};
Константы всех остальных типов данных - в стиле C++ по-Саттеру:
static const double PI = 3.14159;
static const char *PATH = "/usr/local";
Самое главное - учите simplified English. За транслит нужно расстреливать! Но и не злоупотребляйте редко используемыми словами, подобранными через multitran.ru. Всё-таки не исключено, что ваш код будут читать индусы или китайцы, для которых английский также не является родным языком.
На Си приняты безумно_длинные_имена_функций_или_пар_модуль_подчеркивание_метод
, которые можно читать только в виде Python-style имен с подчеркиванием, но не CamelStyle
. Для имен типов данных CamelStyle
допустим, но не рекомендуется.
Для типов данных обычно используется запись с суффиксом _t
. Например:
struct MyStruct {
int field_1;
char field_2;
};
typedef struct MyStruct my_struct_t;
Для переменных давайте понятные имена на английском языке; не нужно делать однобуквенные сокращения. Исключение - общепринятые однобуквенные целочисленные переменные: N
(именно заглавная буква, ибо традиция), i
, j
, k
.
Всем известно, что оператор goto
(дословный перевод на русский язык: иди_на
) строго запрещён в промышленном программировании, а для языка Java - это вообще специально зарезервированное ключевое слово, которое приводит к ошибке компиляции.
Но в системном программировании допускается единственный случай, когда использование goto
целесообразно: завершение работы функции при обработке ошибок, когда требуется гарантированно освободить некоторые ресурсы.
Пример:
int
my_function()
{
char* memory = calloc(BUFFER_SIZE, sizeof(char));
int fd_read = open(PATH, O_RDONLY);
int result = NO_ERROR;
/* ... something unimportant ... */
if (/* error occured */ ) {
result = SOME_ERROR_CODE;
goto Function_End;
}
Function_End:
free(memory);
close(fd_read);
return result;
}
Во всех остальных случаях использовать оператор goto
строго запрещено, как и в высокоуровневом программировании.
На языке Си, в отличии от C++, допускаются массивы переменного размера. Но использовать их можно только в том случае, если их размер заведомо предсказуем.
Вот так допустимо:
enum {N = 100};
char array[N];
Так тоже:
uint8_t N = /* some value, but not more 255 */;
char array[N];
А это - очень плохо:
int N;
scanf("%d", N); /* what if 9999999999999999999 at input? */
char array[N];