Каковы наиболее распространенные соглашения об именах в C?


126

Какие соглашения об именах обычно используются в C? Я знаю, что их как минимум два:

  1. GNU / linux / K&R с функциями lower_case_functions
  2. ? название ? с функциями UpperCaseFoo

Я говорю только о C. Большинство наших проектов - это небольшие встроенные системы, в которых мы используем C.

Вот тот, который я планирую использовать в своем следующем проекте:


C Соглашение об именах

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)

7
Я бы не стал использовать префикс g_ для глобальных переменных; Я бы использовал осмысленные имена (поэтому client_locale, а не cl_lc в качестве имени глобальной переменной). Классический C не использует верблюжий регистр; Я написал код на языке C верблюжьим регистром, и он выглядит странно (так что я больше так не делаю). Тем не менее, это не так - и последовательность важнее того, какое соглашение используется. Избегайте определений типов, которые инкапсулируют указатели на структуры; рассмотрим стандарт C - таким образом пишется «FILE *», а не FILE_PTR.
Джонатан Леффлер,

2
@ Джонатан Леффлер, что не так с g_ для обозначения глобалов? Во встроенных системах у меня раньше были проблемы, в которых было трудно отследить межмодульные зависимости с помощью глобальных переменных и extern g_somevar. Лично я считаю, что это вообще плохая идея, но такие вещи обычно делаются из соображений производительности. Например, глобальный флаг, который устанавливается прерыванием, указывающим, что данные готовы.
JeffV

2
Как бы то ни было, это соглашение об именах в основном было взято из соглашений PalmOS API. Кроме того, это похоже на соглашение, используемое в книге О'Рейли: «Программирование встроенных систем с помощью средств разработки C и GNU». Лично мне нравится TitleCase в именах функций. Я думал использовать lowerCamelCase во внутренних функциях связи (которые я назвал закрытыми в своем вопросе).
JeffV

3
@Chris Lutz, я полностью согласен. По возможности вары должны быть максимально узкими. Обратите внимание, что на самом деле мы обсуждаем три области действия: локальная для функции, локальная для модуля (без внешней привязки к переменной) и глобальные с внешней связью. Во встроенных системах обычно используются переменные, "глобальные для модуля". Следовательно, необходимо позаботиться о том, чтобы идентифицировать глобальные объекты с внешней связью, чтобы их можно было свести к минимуму и понять взаимодействия модулей. Здесь может оказаться полезным префикс "g_".
JeffV

47
Мне нравится ставить перед глобальными переменными префикс //.
plafer

Ответы:


129

Здесь самое главное - последовательность. Тем не менее, я следую соглашению о кодировании GTK +, которое можно резюмировать следующим образом:

  1. Все макросы и константы заглавными буквами: MAX_BUFFER_SIZE, TRACKING_ID_PREFIX.
  2. Имена структур и typedef в camelcase: GtkWidget, TrackingOrder.
  3. Функции, которые работают со структурами: классический стиль C: gtk_widget_show(), tracking_order_process().
  4. Указатели: здесь ничего особенного: GtkWidget *foo, TrackingOrder *bar.
  5. Глобальные переменные: просто не используйте глобальные переменные. Они злые.
  6. Функции, которые есть, но не должны вызываться напрямую или иметь неясное использование, или что-то еще: одно или несколько подчеркиваний в начале: _refrobnicate_data_tables(), _destroy_cache().

13
В шестом пункте я предпочитаю использовать staticи пропускать префикс модуля, поэтому, если бы это gtk_widget_show()была функция с областью файлов, она стала бы просто widget_show()со статическим классом хранения.
Август Карлстром,

27
Дополнительное примечание к пункту 6: в стандарте C есть некоторые правила резервирования имен, начинающихся с, _для реализации и использования в будущем. Есть несколько исключений из имен, начинающихся с, _но, на мой взгляд, их не стоит запоминать. Безопасное правило - никогда не использовать _в коде имена, начинающиеся с . Соответствующая запись в FAQ по C: c-faq.com/~scs/cgi-bin/faqcat.cgi?sec=decl#namespace
jw013

5
# 2 - это, в частности, верхний регистр верблюда или паскаль . Верблюжий регистр или нижний регистр верблюда использует нижний регистр на первой букве.
Клинт Пахл 01

9
А как насчет локальных многословных переменных? my_var или myVar?
Дин Гурвиц

5
Global variables: just don't use global variables. They are evil.- если вы не работаете над встроенным проектом и у вас 1024 байта ОЗУ и процессор 8 МГц.
Камил

30

«Указатели на структуры» не являются объектами, для которых требуется соглашение об именах. Они просто struct WhatEver *. НЕ скрывайте тот факт, что существует указатель, связанный с умным и "очевидным" typedef. Он бесполезен, больше предназначен для ввода и нарушает баланс между объявлением и доступом.


29
+1 за «не скрывать указатели» - хотя этот ответ не затрагивает большую часть остальной части вопроса (пока).
Джонатан Леффлер,

1
@unwind, я склонен согласиться. Однако иногда указатель не предназначен для внешнего разыменования и является скорее дескриптором для потребителя, чем фактическим указателем на структуру, которую они будут использовать. Вот для чего я оставил TitleCasePtr. typedef struct {fields} MyStruct, * MyStructPtr;
JeffV

Я удаляю TitleCasePtr, это отвлекает от фактического вопроса.
JeffV

1
-1 от меня, так как объявление типа указателя уменьшает беспорядок, особенно в сигнатурах функций, а «дисбаланс» между объявлением и доступом проявляется только в файле реализации - клиент не (не должен) напрямую обращаться к членам поля.
Август Карлстром,

1
@AugustKarlstrom Прекрасно. Я не понимаю, что такого «единственного» в файле реализации, разве не тот код? Я не интерпретировал вопрос только как относящийся к «внешним» именам. Весь код на каком-то уровне является «реализацией».
расслабьтесь

17

Ну, во-первых, у C нет публичных / частных / виртуальных функций. Это C ++, и у него другие соглашения. В C обычно есть:

  • Константы в ALL_CAPS
  • Подчеркивание для разделения слов в структурах или именах функций, вы почти никогда не встретите верблюжий регистр в C;
  • структуры, определения типов, союзы, члены (союзов и структур) и значения перечисления обычно находятся в нижнем регистре (по моему опыту), а не в соглашении C ++ / Java / C # / etc о том, чтобы сделать первую букву заглавной, но я думаю, что это возможно в C тоже.

C ++ более сложен. Я видел здесь настоящий микс. Верблюжий регистр для имен классов или строчные буквы + подчеркивания (по моему опыту чаще встречается верблюжий регистр). Структуры используются редко (и обычно потому, что они требуются библиотеке, иначе вы бы использовали классы).


@cletus, я это понимаю. Под частными я подразумеваю функции, которые не отображаются извне в заголовке модуля и не предназначены для использования внешним по отношению к модулю кодом. Общедоступными будут функции API модуля, предназначенные для внешнего использования.
JeffV

3
Вы можете рассматривать статические функции как частные; вопрос не упоминает виртуальный. Но +1 за «редко вижу верблюжий футляр в C».
Джонатан Леффлер,

2
Я думаю, Джефф имел в виду external linkage«публичные функции» и internal linkage«частные функции».
pmg

1
Я видел константы, начинающиеся с ak, а также в: kBufferSize. Не уверен, откуда это взялось.
JeffV

2
ALL_CAPSтакже часто используется для значений перечисления.
caf

15

Знаете, мне нравится, когда это просто, но понятно ... Итак, вот что я использую в C:

  • Тривиальные переменные : i,n,cи т. Д. (Только одна буква. Если одна буква непонятна, сделайте ее локальной переменной)
  • Локальные переменные :lowerCamelCase
  • Глобальные переменные :g_lowerCamelCase
  • Постоянные переменные :ALL_CAPS
  • Переменные указателя : добавьте p_к префиксу. Для глобальных переменных это будет gp_varдля локальных переменных p_var, для константных переменных p_VAR. Если используются дальние указатели, используйте fp_вместо p_.
  • Структуры : ModuleCamelCase(Модуль = полное имя модуля или сокращение из 2–3 букв, но все еще внутри CamelCase.)
  • Переменные элементов структуры :lowerCamelCase
  • Перечисляет :ModuleCamelCase
  • Значения перечисления :ALL_CAPS
  • Публичные функции :ModuleCamelCase
  • Частные функции :CamelCase
  • Макросы :CamelCase

Я набираю свои структуры, но использую одно и то же имя как для тега, так и для typedef. Тег не предназначен для широкого использования. Вместо этого предпочтительнее использовать typedef. Я также передаю объявление typedef в заголовке общедоступного модуля для инкапсуляции, чтобы я мог использовать имя typedef'd в определении.

Полный struct пример :

typdef struct TheName TheName;
struct TheName{
    int var;
    TheName *p_link;
};

Я ничего не знаю о фреймворке qt, но вы можете написать свой код в любом формате стиля, который захотите. Насколько я знаю, вас ничто не удерживает от этого.
SeanRamey

10

Кодируя одновременно C #, java, C, C ++ и объект C , я принял очень простое и понятное соглашение об именах, чтобы упростить себе жизнь.

Прежде всего, он опирается на мощь современных IDE (таких как eclipse, Xcode ...), с возможностью быстро получать информацию, наведя курсор мыши или нажав ctrl ... Принимая это, я запретил использование любого префикса, суффикса и другие маркеры, которые просто выдаются IDE.

Затем соглашение:

  • Любые имена ДОЛЖНЫ быть удобочитаемыми предложениями, объясняющими, что у вас есть. Типа «это моя конвенция».
  • Затем 4 метода, позволяющие получить условность из предложения:
    1. THIS_IS_MY_CONVENTION для макросов, членов перечисления
    2. ThisIsMyConvention для имени файла, имени объекта (класс, структура, перечисление, объединение ...), имени функции, имени метода, typedef
    3. this_is_my_convention глобальные и локальные переменные,
      параметры, элементы структуры и объединения
    4. thisismyconvention [необязательно] очень локальные и временные переменные (например, индекс цикла for ())

И это все.

Это дает

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;
        }
    }
}

8

Я бы рекомендовал не смешивать случай верблюда и разделение подчеркивания (как вы предлагали для членов структуры). Это смущает. Вы бы подумали: «Привет, у меня есть, get_lengthтак что мне, наверное, следовало бы попробовать», make_subsetи тогда вы узнаете, что это действительно так makeSubset. Используйте принцип наименьшего удивления и будьте последовательны.

Я считаю CamelCase полезным для ввода имен, таких как структуры, определения типов и перечисления. Но это все. Для всего остального (имена функций, имена членов структуры и т. Д.) Я использую underscore_separation.


1
Да, главное в любом соглашении об именах - это предсказуемость и последовательность. Кроме того, поскольку сама библиотека C использует все строчные буквы с _ для пробелов, я бы рекомендовал использовать это, чтобы вам не приходилось иметь дело с двумя различными соглашениями об именах в проекте (при условии, что вы не пишете оболочку вокруг libc, чтобы сделать это соответствует вашему названию .. но это
мерзко

Он также использует typedef с буквой « t» на конце, но я не вижу, чтобы кто-то рекомендовал это. Фактически, стандартная библиотека даже несовместима: div_t (stdlib.h) является структурой, как и tm (time.h). Кроме того, взгляните на члены структуры tm, все они имеют префикс tm, что кажется бессмысленным и уродливым (IMO).
JeffV

1
«Я считаю CamelCase полезным для ввода имен ...» Если вы начнете его с заглавной буквы, это на самом деле PascalCase.
Tagc

7

Вот (по-видимому) необычный, который я нашел полезным: имя модуля в CamelCase, затем подчеркивание, затем имя функции или области видимости файла в CamelCase. Так например:

Bluetooth_Init()
CommsHub_Update()
Serial_TxBuffer[]

2
Не так уж необычно, но очень полезно.
chux

3

Меня смущает одно: вы планируете создать новое соглашение об именах для нового проекта. Как правило, у вас должно быть соглашение об именах для всей компании или группы. Если у вас уже есть проекты, которые имеют какое-либо соглашение об именах, вам не следует менять соглашение для нового проекта. Если приведенное выше соглашение является просто кодификацией ваших существующих практик, тогда вы золотой. Чем больше он будет отличаться от существующих стандартов де-факто, тем труднее будет получить представление о новом стандарте.

Единственное, что я хотел бы добавить, - мне понравился _t в конце типов в стиле uint32_t и size_t. Для меня это очень C-ish, хотя некоторые могут пожаловаться, что это просто "обратный" венгерский.


3
Что ж, условности здесь повсюду и непоследовательны, поэтому я собираюсь задокументировать их. Кроме того, вот почему я спрашиваю. Чтобы узнать, что такое консенсус сообщества.
JeffV

Я понимаю эту боль. Но должно быть какое-то подмножество ваших существующих соглашений, которое будет наиболее популярным. Вы должны начать с него, а не со случайной веб-страницы в Интернете. Также вы должны спросить своих других разработчиков, что они считают хорошим.
jmucchiello

7
Я считаю, что имена типов, заканчивающиеся на _t, зарезервированы стандартом POSIX.
caf

4
Имя, заканчивающееся на _t, зарезервировано. См gnu.org/software/libc/manual/html_node/Reserved-Names.html , «Имена , которые заканчиваются„_t“зарезервированы для дополнительных имен типа.»
Étienne

2

Вы также должны подумать о порядке слов, чтобы упростить автоматическое завершение имени .

Хорошая практика: имя библиотеки + имя модуля + действие + тема

Если часть не актуальна, просто пропустите ее, но всегда должны быть представлены хотя бы имя модуля и действие.

Примеры:

  • Имя функции: os_task_set_prio, list_get_size,avg_get
  • define (здесь обычно нет части действия ):OS_TASK_PRIO_MAX

0

Их может быть много, в основном IDE диктуют некоторые тенденции, и соглашения C ++ также продвигаются. Для C обычно:

  • UNDERSCORED_UPPER_CASE (определения макросов, константы, члены перечисления)
  • underscored_lower_case (переменные, функции)
  • CamelCase (настраиваемые типы: структуры, перечисления, объединения)
  • uncappedCamelCase (стиль OPPA Java)
  • UnderScored_CamelCase (переменные, функции в виде пространств имен)

Венгерская нотация для глобальных переменных подходит, но не для типов. И даже для банальных имен используйте хотя бы два символа.


-1

Я думаю, это может помочь новичку: соглашение об именах переменных в c

  1. Вы должны использовать алфавитный символ (az, AZ), цифру (0-9) и меньшую оценку (_). Нельзя использовать какие-либо специальные символы, такие как:%, $, #, @ и т. Д. Таким образом, вы можете использовать user_name как переменную, но не можете использовать user & name .
  2. Нельзя использовать пробелы между словами. Таким образом, вы можете использовать user_name, или имя пользователя, или имя пользователя в качестве переменной, но не можете использовать имя пользователя .
  3. Не могу начать именование с цифры. Таким образом, вы можете использовать user1 или user2 как переменную, но не можете использовать 1user .
  4. Это язык с учетом регистра. Прописные и строчные буквы имеют значение. Если вы используете такую ​​переменную, как имя пользователя, вы не можете использовать ИМЯ ПОЛЬЗОВАТЕЛЯ или Имя пользователя для использования отцом.
  5. Вы не можете использовать любое ключевое слово (char, int, if, for, while и т. Д.) Для объявления переменных.
  6. Стандарт ANSI распознает длину 31 символа для имени переменной.

Этот пост явно направлен на обсуждение соглашений об именах , а не ограничений . То, что вы перечислили, вы даже не можете сделать, потому что это синтаксические ошибки.
Чейз
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.