Как мне структурировать сложные проекты на C? [закрыто]

79

У меня немного больше, чем навыки C на начальном уровне, и я хотел бы знать, существуют ли какие-либо де-факто «стандарты» для структурирования довольно сложных приложений на C. Даже на основе графического интерфейса пользователя.

Я всегда использовал парадигму объектно-ориентированного программирования в Java и PHP, и теперь, когда я хочу изучить C, я боюсь, что могу неправильно структурировать свои приложения. Я не понимаю, каким руководящим принципам следовать, чтобы иметь модульность, разделение и сухость с процедурным языком.

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

Стивен
источник
3
Философия Unix очень полезна для организации больших проектов: http://www.faqs.org/docs/artu/ch01s06.html
newDelete

Ответы:

51

Ключ - модульность. Это проще спроектировать, реализовать, скомпилировать и поддерживать.

  • Определите модули в своем приложении, например классы в объектно-ориентированном приложении.
  • Отдельный интерфейс и реализация для каждого модуля, вставьте в интерфейс только то, что нужно другим модулям. Помните, что в C нет пространства имен, поэтому вам нужно сделать все в ваших интерфейсах уникальным (например, с префиксом).
  • Скрыть глобальные переменные в реализации и использовать функции доступа для чтения / записи.
  • Не думайте о наследовании, а о композиции. Как правило, не пытайтесь имитировать C ++ в C, это будет очень трудно читать и поддерживать.

Если у вас есть время на обучение, посмотрите, как структурировано приложение Ada, с его обязательными package(интерфейс модуля) и package body(реализация модуля).

Это для кодирования.

Для поддержки (помните, что вы кодируете один раз, но поддерживаете несколько раз) я предлагаю задокументировать ваш код; Doxygen - хороший выбор для меня. Я также предлагаю создать надежный набор регрессионных тестов, который позволит вам провести рефакторинг.

Mouviciel
источник
30

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

Одна из основ проектирования надежной системы - это инкапсуляция реализации за интерфейсом. FILE*и функции, которые с ним работают ( fopen()и fread()т. д.), являются хорошим примером того, как инкапсуляция может применяться в C для создания интерфейсов. (Конечно, поскольку C не имеет спецификаторов доступа, вы не можете обеспечить, чтобы никто не заглядывал внутрь a struct FILE, но только мазохист мог бы это сделать.)

При необходимости полиморфное поведение может быть реализовано в C с использованием таблиц указателей на функции. Да, синтаксис уродливый, но эффект такой же, как у виртуальных функций:

j_random_hacker
источник
11
Программист на чистом языке C потеряется, читая этот код ...
mouviciel
15
@mouviciel: Чушь! Большинство программистов на C понимают указатели на функции (или, по крайней мере, должны), и кроме этого здесь ничего не происходит. По крайней мере, в Windows драйверы устройств и COM-объекты обеспечивают свои функции таким образом.
j_random_hacker
8
Я говорю не о некомпетентности, а о ненужных сложностях. Указатели функций являются общими для кодировщика C (например, обратные вызовы), наследование - нет. Я предпочитаю, чтобы кодировщик C ++, кодирующий на C, использовал свое время для изучения C, чем для создания псевдо-классов C ++. Тем не менее, в некоторых случаях ваш подход может быть полезен.
mouviciel
3
На самом деле вы даже можете смоделировать спецификаторы доступа, используя идиому C ++ pimpl. Если закрытые члены типа инкапсулированы в тип, который виден только в реализации (он же «файл .c»), пользователям интерфейса будет сложно их изменить (конечно, они могут писать мусор в указатель pimpl, если они хотят вас умышленно обмануть , но вы можете сделать то же самое на C ++).
bitmask
2
Голосующий против: хотите прокомментировать?
j_random_hacker
15

Все хорошие ответы.

Я бы только добавил «минимизировать структуру данных». Это может быть даже проще в C, потому что если C ++ - это «C с классами», ООП пытается побудить вас взять каждое существительное / глагол в своей голове и превратить его в класс / метод. Это может быть очень расточительным.

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

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

Таким образом, вы получаете огромное количество кода, который настолько удобочитаем, что просто тратит 90% своего времени на управление объектами.

Все это делается во имя «хорошей практики программирования» и «эффективности».

По крайней мере, в C простой и эффективный способ будет более очевидным, а искушение построить пирамиды менее сильным.

Майк Данлэйви
источник
1
Мне нравится ваш ответ, и это чистая правда. ООП должно умереть!
Jo So
@JoSo: Я использую ООП, но минимально.
Майк Данлэйви
Для меня «минимально» (хотя я не знаю, что это значит для вас) на самом деле не считается ООП, то есть объектно- ориентированным программированием - объектами по умолчанию.
Jo So
Я тоже использую "ООП". В основном для моих модулей C, многие из которых имеют подпрограммы init () и exit ().
Jo So
11

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


источник
4
Не всем они нравятся, из lxr.linux.no/linux+v2.6.29/Documentation/CodingStyle : «Во-первых, я бы предложил распечатать копию стандартов кодирования GNU и НЕ читать ее. Сожгите их, это великий символический жест ». Я не читал их много лет, но у Линуса есть веские возражения.
hlovdal
1
@hlovdal: Конечно, не всем нравится какой-то конкретный стандарт кодирования, поэтому существует более одного стандарта для подобных случаев использования. Важная часть состоит в том, чтобы вы были последовательны в ваших собственных проектах, чтобы соблюдались хотя бы некоторые стандарты, а не фактическое несогласованность.
JM Becker
4

Если вы знаете, как структурировать свой код на Java или C ++, вы можете следовать тем же принципам с кодом C. Единственное отличие состоит в том, что у вас нет компилятора и вам нужно делать все вручную вручную.

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

У вас не может быть классов с C, но вы можете легко реализовать «абстрактные типы данных». Вы создаете файлы .C и .H для каждого абстрактного типа данных. Если вы предпочитаете, у вас может быть два файла заголовков, один публичный и один частный. Идея состоит в том, что все структуры, константы и функции, которые необходимо экспортировать, попадают в общедоступный файл заголовка.

Ваши инструменты тоже очень важны. Полезным инструментом для C является lint , который может помочь вам найти неприятный запах в вашем коде. Еще один инструмент, который вы можете использовать, - это Doxygen, который может помочь вам в создании документации .

Кгианнакакис
источник
4

Инкапсуляция всегда является ключом к успешной разработке, независимо от языка разработки.

Уловка, которую я использовал для инкапсуляции «частных» методов в C, заключается в том, чтобы не включать их прототипы в файл «.h».

Нейт
источник
3

Я предлагаю вам ознакомиться с кодом любого популярного проекта C с открытым исходным кодом, например ... хм ... ядра Linux или Git; и посмотрите, как они это организуют.

Иван Кречетов
источник
3

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

Чтобы упростить сложное приложение, я использую « Разделяй и властвуй» .

MrValdez
источник
2

Я бы посоветовал в качестве первого шага прочитать учебник C / C ++. Например, C Primer Plus - хороший пример. Просмотр примеров даст вам представление о том, как сопоставить ваш объектно-ориентированный объект Java с более процедурным языком, таким как C.

Тоса
источник