Соглашения об именах, используемые для переменных и функций в C [закрыто]

13

При кодировании большого проекта в CI возникла проблема. Если я продолжу писать больше кода, то будет время, когда мне будет трудно организовать код. Я имею в виду, что именование функций и переменных для разных частей программы может показаться смешанным.

Поэтому я подумал, есть ли полезные соглашения об именах, которые я могу использовать для переменных и функций Си?

Большинство языков предлагают соглашение об именах. Но для C единственное, что я до сих пор читал, это то, что имена должны быть описательными для удобства чтения кода.

РЕДАКТИРОВАТЬ:

Примеры некоторых примеров предлагаемых соглашений об именах:

Я читал еще несколько соглашений об именах для Java, но не мог вспомнить где.

Aseem Bansal
источник
Приведите несколько примеров языков с предлагаемыми соглашениями об именах. И где мы можем найти эти соглашения об именах.
Филипп
@Philip Добавил примеры
Aseem Bansal
1
Не должно быть проблем с переменными, поскольку вы не используете глобальные переменные. И для имен функций: если имя модуля order.c, вы можете назвать функции order_add(), order_del()и тому подобное. Могут быть старые системы, которые сообщают вам, что имя должно быть уникальным в пределах первых 8 символов. Когда вы случайно переключитесь на c ++ позже, вам понравится писать order::add()и order::del()тогда.
ott--

Ответы:

17

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

Это ваша проблема: выправьте организацию правильно, и стиль должен развиваться легче.

Не ждите, чтобы организовать свой код: держите ваш код организованным на ходу. Хотя язык не делает этого за вас, код все равно должен быть организован в модули с низкой связью и высокой связностью.

Эти модули затем естественно предоставляют пространство имен. Сократите имя модуля (если оно длинное) и добавьте к имени модуля префиксные функции, чтобы избежать коллизий.

На уровне отдельных идентификаторов они примерно в порядке возрастания субъективности:

  1. выбрать конвенцию и придерживаться ее
    • например, function_like_this(struct TypeLikeThis variable)распространено
  2. определенно избегайте венгерских обозначений (извините, JNL)

    • если вы не хотите использовать его так, как предполагалось изначально, что означает нотацию приложений Simonyi, а не ужасную версию системы

      Почему? Я мог бы написать эссе об этом, но вместо этого я предлагаю вам прочитать эту статью Джоэла Спольски, а затем поохотиться еще немного, если вам интересно. Внизу есть ссылка на оригинальную статью Симони.

  3. избегайте указателей typedefs, если они не являются действительно непрозрачными типами cookie - они только запутывают вещи

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

    Что я подразумеваю под непрозрачным типом печенья ? Я имею в виду то, что используется внутри модуля (или библиотеки, или чего-то еще), которое должно быть передано клиентскому коду, но этот клиентский код не может использоваться напрямую. Он просто передает его обратно в библиотеку.

    Например, библиотека базы данных может предоставлять такой интерфейс, как

    /* 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);
    

    Теперь контекст непрозрачен для клиентского кода, потому что вы не можете заглянуть внутрь. Вы просто передаете это обратно в библиотеку. Нечто подобное FILEтакже непрозрачно, и целочисленный файловый дескриптор также является cookie , но не непрозрачен.


Примечание по дизайну

Я использовал фразу « слабая связь и высокая сплоченность» выше без объяснения причин, и я чувствую себя немного плохо по этому поводу. Вы можете найти его и, возможно, найти хорошие результаты, но я постараюсь кратко остановиться на нем (опять же, я мог бы написать эссе, но постараюсь не делать этого).

Библиотека DB, показанная выше, показывает слабую связь, потому что она предоставляет маленький интерфейс для внешнего мира. Скрывая детали своей реализации (частично с помощью непрозрачного трюка с cookie), он предотвращает зависимость кода клиента от этих деталей.

Представьте, что вместо непрозрачного cookie-файла мы объявляем структуру контекста, чтобы ее содержимое было видимым, включая дескриптор файла сокета для соединения TCP с базой данных. Если впоследствии мы изменим реализацию для поддержки использования сегмента общей памяти, когда БД работает на том же компьютере, клиент должен быть перекомпилирован, а не просто перекомпонован. Хуже того, клиент мог начать использовать файловый дескриптор, например, вызвать setsockoptизменение размера буфера по умолчанию, и теперь ему также нужно изменить код. Все эти детали должны быть скрыты внутри нашего модуля, где это практически возможно, и это дает низкую связь между модулями.

В примере также показана высокая степень согласованности , поскольку все методы модуля связаны с одной и той же задачей (доступ к БД). Это означает, что только код, который должен знать о деталях реализации (то есть о содержимом нашего cookie), фактически имеет к ним доступ, что упрощает отладку.

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

Теперь сказать, что этот пример хороший, легко (особенно потому, что он еще не закончен), но не сразу вам поможет. Хитрость заключается в том, чтобы следить за тем, как вы пишете и расширяете свой код, за функциями, которые выполняют сходные функции или работают с одними и теми же типами (которые могут быть кандидатами для своего собственного модуля), а также за функциями, которые выполняют множество отдельных вещей, которые Это действительно связано, и может быть кандидатами на разделение.

Бесполезный
источник
Можете ли вы помочь мне понять, почему избегают венгерского? Просто любопытно узнать больше об этом. :)
JNL
@JNL: комментарий слишком короткий, чтобы правильно объяснить. Я предлагаю вам опубликовать его как новый вопрос.
Барт ван Инген Шенау
with low coupling and high cohesion, Что это обозначает? И, пожалуйста, объясните о непрозрачных типах печенья. Я понятия не имею, что это значит.
Aseem Bansal
Я попытался ответить как кратко, так и откровенно не смог на краткость. Надеюсь, это поможет вам начать.
бесполезно
Я отвечаю через несколько дней. Простите за это. Я прочитал ваше описание low coupling and high cohesion. Так что это в основном означает инкапсулировать вещи, когда я могу, и это должно быть сделано таким образом, чтобы функции, к которым действительно нужно, имели доступ. Некоторые вещи пролетели над моей головой, но все же я думаю, что понял вашу точку зрения.
Aseem Bansal
5

На мой взгляд, 90% проблемы именования решается, если вы помните три вещи: а) сделать имена переменных и функций как можно более описательными, б) быть последовательными во всем коде (т. Е. Если функция называется addNumbers, вторая функция должна называться multiplyNumbers, а не numbersMul) и c) стараться делать имена короткими, если это возможно, так как нам нужно их набирать.

При этом, если вы хотите взглянуть на другие аспекты этой темы, на странице Википедии по соглашениям об именах есть хороший список вещей, которые вы должны иметь в виду. Он также имеет раздел на C и C ++:

В C и C ++ ключевые слова и идентификаторы стандартной библиотеки в основном строчные. В стандартной библиотеке C сокращенные имена являются наиболее распространенными (например, isalnum для функции, проверяющей, является ли символ буквенно-цифровым), в то время как стандартная библиотека C ++ часто использует подчеркивание в качестве разделителя слов (например, out_of_range). Идентификаторы, представляющие макросы, по соглашению пишутся с использованием только заглавных букв и подчеркиваний (это связано с соглашением во многих языках программирования об использовании идентификаторов всех заглавных букв для констант). Имена, содержащие двойное подчеркивание или начинающиеся с подчеркивания и заглавной буквы, зарезервированы для реализации (компилятор, стандартная библиотека) и не должны использоваться (например, зарезервированы__ или _Reserved). [5] [6] Это внешне похоже на обрезку, но семантика отличается:

Даниэль Скокко
источник
3
«постарайтесь сделать имена короткими, если это возможно». Используйте IDE с автозаполнением, тогда имена ваших функций могут быть настолько длинными и описательными, насколько это необходимо, поскольку вам нужно набирать их только один раз.
Джоэл
1
@ Джоэл ужасный совет. Не все будут использовать ту же IDE, что и вы.
Джеймс
6
@James Им не нужно, они могут просто использовать любую приличную IDE. Тогда вам не нужно жертвовать ясностью ради производительности.
Джоэл
Термин IDE растянулся на несколько дней. Технически Notepad ++ - это IDE, потому что вы можете настроить его для компиляции и запуска проекта, но в первую очередь это текстовый редактор. И это автоматически завершается.
Филипп
5

Единственное жесткое ограничение в C - это отсутствие пространств имен. Следовательно, вы должны найти способ сделать rename()функцию библиотеки вашей файловой системы отличной от rename()функции вашей библиотеки мультимедиа . Обычным решением является префикс, такой как: filesystem_rename()и media_rename().

Другой общий совет: оставайтесь последовательными в рамках проекта или команды. Читаемость будет улучшена.

mouviciel
источник
+1: это особенно верно для экспортируемых символов в библиотеке. «Извините, но эта библиотека файловой системы не идет с этой библиотекой мультимедиа, потому что обе имеют экспортированную функцию переименования.
Residuum
2

ЕСЛИ ВЫ ИЩЕТЕ ГЛОБАЛЬНО ПРИНЯТЫЙ ФОРМАТ

MISRA / JSF / AUTOSAR покрывает почти 100% всех отраслевых стандартов по именованию и организации кода C / C ++. Проблема в том, что они не будут бесплатными, т. Е. Каждый путеводитель стоит денег. Я знаю, что стандартная книга по кодированию MISRA 2008 C / C ++, вероятно, стоит около 50 долларов США.

Вы можете думать о них как о Гарвардских ссылках для библиографии и дополнительного чтения, когда вы пишете журнал. Я использовал MISRA, и это хороший способ назвать ваши функции и переменные и организовать их для правильного использования.

ЕСЛИ ВЫ ИЩЕТЕ ЧТО-ТО ВРЕМЕННОЕ

Думаю, ссылки на Python и Java в порядке. Я видел людей, принимающих комментирование, именование и организацию кода в стиле javadoc. На самом деле, в моем последнем проекте мне пришлось писать код C ++ в Java-подобных функциях / именах переменных. Две причины этого:

1) Это было очевидно легче следовать.

2) Требования к производственному коду не касались основ стандартов безопасности программного обеспечения.

3) Устаревший код был (как-то) в этом формате.

4) Doxygen разрешил комментировать Javadoc sytle. В тот момент мы использовали doxygen для создания документации для производственных парней.

Многие программисты будут противостоять этому, но лично я считаю, что нет ничего плохого в том, чтобы использовать в Java / C ++ имя функции / именования переменных в стиле javadoc. ДА КУРС, методы организации управления потоком, безопасности потоков и т. Д. Необходимо решать независимо. Тем не менее, я не претендент здесь. Я также не знаю, насколько строги ваши требования к формату производственного кода. Не отвлекая его на не относящиеся к теме области, я предлагаю вам ознакомиться с вашими требованиями, выяснить, насколько вы зависели от конкретного соглашения об именах, и воспользоваться решением, упомянутым в моих и других ответах.

Надеюсь, это помогло !?

hagubear
источник
На самом деле я просил это для личных кодов Си. Но я запомню ваше предложение.
Aseem Bansal
@AseemBansal Личный или профессиональный, это хорошо учиться, а также хорошо, чтобы положить на свое резюме :) .... до вас.
hagubear
0

Несколько важных вещей, которые следует учитывать при именовании;

  1. Посмотрите на тип actionObject или ObjectAction. (Объект не для C. Но в целом, когда вы переходите на другие объектно-ориентированные языки) Это должно помочь

  2. Отдых был бы последовательным, коротким и описательным наверняка.

  3. Кроме того, единственная цель каждой определенной переменной и функции, например: если ее временно сохранить значение, назовите его как nTempVal для int
  4. Переменные должны быть существительными, а методы - глаголом.
JNL
источник
6
Венгерская нотация (префикс переменной с буквами, обозначающими тип) не дает конца боли. К счастью, в основном это вышло из моды.
Gort the Robot
@ StevenBurnap Было просто любопытно, почему избегают венгерского формата? Я считаю, что это то, чему нас учили в школе, и я видел такой кодекс и на некоторых рабочих местах. Какой из них вы бы порекомендовали, если не венгерский. Спасибо
JNL
1
Наилучшим соглашением об именах является только одно последовательно используемое, с четкими описательными именами, которые в идеале должны быть относительно короткими без чрезмерного сокращения и избегания избыточных префиксов. Венгерская нотация практически не используется, затрудняет чтение кода и усложняет изменение типов.
Gort Робот
2
Вот описание первоначального намерения и мерзость , что Венгерская нотация стала: joelonsoftware.com/articles/Wrong.html
Residuum
@Residuum Это была хорошая ссылка. Очень помогло. Ценить это.
JNL
0

Большинство ответов хорошие, но я хочу сказать кое-что о соглашениях об именах для библиотек и включаемых файлов, аналогично использованию пространств имен в других языках, таких как C ++ или Java:

Если вы создаете библиотеку, найдите общий префикс для ваших экспортируемых символов, то есть глобальных функций, typedefs и переменных. Это предотвратит конфликты с другими библиотеками и определит функции как исходящие от вас. Это немного приложений венгерских обозначений.

Может быть, пойти еще дальше и сгруппировать экспортируемые символы: libcurl использует curl_ * для глобальных символов, curl_easy_ *, curl_multi_ * и curl_share_ * для различных интерфейсов. Поэтому в дополнение к использованию curl_ * для всех функций они добавили еще один уровень «пространств имен» для различных интерфейсов: вызов функции curl_easy_ * для дескриптора curl_multi_ * теперь выглядит неправильно, см. Имена функций по адресу http: // curl. haxx.se/libcurl/c/

Сохраняя правила для экспортируемых символов, вы должны использовать их для статических функций в #includeфайлах ed: Попробуйте найти общий префикс для этих функций. Может быть, у вас есть статические строковые функции в файле с именем "my_string"? Префикс всех этих функций с my_string_ *.

отстой
источник
Под экспортированными символами вы понимаете глобальные переменные, функции, определения типов и т. Д., Если я прав. Можете ли вы объяснить немного о группировке экспортируемых символов? Я думал, вы уже объяснили это в предыдущем абзаце. Что вы добавили в 3-й абзац?
Aseem Bansal