2009-03-19 1 views
64

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

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

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

+2

Философия Unix очень полезна для организации крупных проектов: [http://www.faqs.org/docs /artu/ch01s06.html](http://www.faqs.org/docs/artu/ch01s06.html) – newDelete

ответ

2

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

2

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

Чтобы упростить сложное приложение, я использую Divide and conquer.

43

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

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

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

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

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

10

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

+4

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

+0

@hlovdal: Конечно, не всем нравится какой-либо конкретный стандарт кодирования, поэтому для подобных вариантов использования существует более одного стандарта. Важная часть состоит в том, что вы согласуетесь с вашими собственными проектами, чтобы следовать хотя бы некоторым стандартам, а не фактической несогласованности. – TechZilla

27

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

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

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

struct IAnimal { 
    int (*eat)(int food); 
    int (*sleep)(int secs); 
}; 

/* "Subclass"/"implement" IAnimal, relying on C's guaranteed equivalence 
* of memory layouts */ 
struct Cat { 
    struct IAnimal _base; 
    int (*meow)(void); 
}; 

int cat_eat(int food) { ... } 
int cat_sleep(int secs) { ... } 
int cat_meow(void) { ... } 

/* "Constructor" */ 
struct Cat* CreateACat(void) { 
    struct Cat* x = (Cat*) malloc(sizeof (struct Cat)); 
    x->_base.eat = cat_eat; 
    x->_base.sleep = cat_sleep; 
    x->meow = cat_meow; 
} 

struct IAnimal* pa = CreateACat(); 
pa->eat(42);      /* Calls cat_eat() */ 

((struct Cat*) pa)->meow();  /* "Downcast" */ 
+10

Чистый C-кодер потеряется, прочитав этот код ... – mouviciel

+14

@mouviciel: Мусор! Большинство кодеров C понимают указатели на функции (или, по крайней мере, должны), и здесь ничего не происходит. В Windows, по крайней мере, драйверы устройств и COM-объекты обеспечивают их функциональность таким образом. –

+8

Моя мысль не о некомпетентности, это о ненужном осложнении. Указатели функций являются общими для C-кодера (например, обратные вызовы), наследование - нет. Я предпочитаю, чтобы кодирование C++-кода в C использовало свое время для изучения C, чем для создания псевдо-классов C++. Тем не менее, ваш подход может быть полезен в некоторых случаях. – mouviciel

3

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

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

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

Ваши инструменты также очень важны. Полезным инструментом для C является lint, который может помочь вам найти неприятные запахи в вашем коде. Другим инструментом, который вы можете использовать, является Doxygen, который может помочь вам генерировать documentation.

2

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

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

12

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

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

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

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

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

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

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

+0

Любите свой ответ, и это абсолютно так. ООП должен умереть! –

+0

@JoSo: Я использую ООП, но минимально. –

+0

Мне «минимально» (хотя я не знаю, что это значит для вас) на самом деле не считается ООП, который по объекту Object * oriented * programming-objects по умолчанию. –

2

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

Смежные вопросы