Як слід структурувати складні проекти на мові C? [зачинено]


79

Я маю трохи більше, ніж навички початкового рівня С, і хотів би знати, чи існують фактичні "стандарти" для структурування дещо складного застосування в С. Навіть такі, що базуються на графічному інтерфейсі.

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

Чи можете ви запропонувати якісь показання? Я не міг знайти жодної програми для C, навіть якщо я не використовую фреймворки, я завжди знаходив хороші ідеї, переглядаючи їх код.


3
Філософія Unix досить корисна для організації великих проектів: http://www.faqs.org/docs/artu/ch01s06.html
newВидалення

Відповіді:


51

Ключ - модульність. Це простіше розробити, впровадити, скомпілювати та підтримувати.

  • Визначте модулі у своєму додатку, як класи в додатку OO.
  • Окремий інтерфейс та реалізація для кожного модуля, додайте в інтерфейс лише те, що потрібно іншим модулям. Пам'ятайте, що в C немає простору імен, тому ви повинні зробити все у своїх інтерфейсах унікальним (наприклад, з префіксом).
  • Приховати глобальні змінні в реалізації та використовувати функції доступу для читання / запису.
  • Не думайте з точки зору спадщини, а з точки зору складу. Як загальне правило, не намагайтеся імітувати C ++ в C, це буде дуже важко читати та підтримувати.

Якщо у вас є час для навчання, подивіться, як структурована програма Ada, з її обов’язковими package(інтерфейс модуля) та package body(реалізація модуля).

Це для кодування.

Для підтримки (пам’ятайте, що ви кодуєте один раз, але ви підтримуєте кілька разів), я пропоную задокументувати свій код; Кисень - приємний вибір для мене. Я пропоную також створити потужний набір тестів регресії, який дозволяє вам рефакторинг.


30

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

Однією з основ надійного проектування системи є інкапсуляція реалізації за інтерфейсом. FILE*і функція , що робота з ним ( fopen(), і fread()т.д.) є хорошим прикладом того , як капсулирование може бути застосоване в C , щоб встановити інтерфейси. (Звичайно, оскільки у C відсутні специфікатори доступу, ви не можете примусити когось заглядати всередину struct FILE, але це зробив би лише мазохіст.)

При необхідності поліморфну ​​поведінку можна мати в C, використовуючи таблиці покажчиків на функції. Так, синтаксис потворний, але ефект такий самий, як і віртуальні функції:


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: Я використовую ООП, але мінімально.
Mike Dunlavey

Для мене "мінімально" (хоча я не знаю, що це для вас означає) насправді не вважається ООП, тобто об'єктно- орієнтоване програмування - об'єкти за замовчуванням.
Джо Со

Я також використовую деякі "ООП". В основному для моїх C-модулів, багато з яких мають процедури init () та exit ().
Джо Со

11

Стандарти кодування GNU розвивалися протягом декількох десятиліть. Було б непогано їх прочитати, навіть якщо ви не дотримуєтесь їх до листа. Думаючи про підняті в них моменти, ви отримуєте більш тверду основу щодо того, як структурувати власний код.


4
Вони подобаються не всім, починаючи з lxr.linux.no/linux+v2.6.29/Documentation/CodingStyle : "По-перше, я пропоную роздрукувати копію стандартів кодування GNU, а НЕ читати. Запишіть їх, це великий символічний жест ". Я не читав їх багато років, але Лінус має кілька вагомих заперечень.
hlovdal

1
@hlovdal: Звичайно, не всім подобається якийсь конкретний стандарт кодування, тому існує більше одного стандарту для подібних випадків використання. Важливим є те, що ви відповідаєте власним проектам, дотримуєтесь хоча б якогось стандарту, а не фактично не узгоджуєтеся спеціально.
Дж. М. Беккер

4

Якщо ви знаєте, як структурувати свій код на Java або C ++, тоді ви можете слідувати тим самим принципам і на коді C. Єдина відмінність полягає в тому, що у вас немає компілятора поруч, і вам потрібно робити все дуже обережно вручну.

Оскільки пакунків та класів немає, для початку потрібно ретельно розробити свої модулі. Найпоширенішим підходом є створення окремої вихідної папки для кожного модуля. Вам потрібно покладатися на правила іменування для розмежування коду між різними модулями. Наприклад, додайте всі функції до імені модуля.

Ви не можете мати класи з C, але ви можете легко реалізувати "Абстрактні типи даних". Ви створюєте файли .C та .H для кожного абстрактного типу даних. Якщо ви бажаєте, ви можете мати два файли заголовків, один загальнодоступний і один приватний. Ідея полягає в тому, що всі структури, константи та функції, які потрібно експортувати, переходять до загальнодоступного файлу заголовка.

Ваші інструменти також дуже важливі. Корисним інструментом для C є ворсинки , які можуть допомогти вам знайти поганий запах у вашому коді. Іншим інструментом, яким ви можете скористатися, є Doxygen, який допоможе вам створити документацію .


4

Інкапсуляція завжди є ключем до успішного розвитку, незалежно від мови розробки.

Фокус, який я використав, щоб допомогти інкапсулювати "приватні" методи в C, полягає в тому, щоб не включати їх прототипи у файл ".h".


3

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


3

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

Щоб спростити складні програми, я використовую Divide and conquer .


2

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.