Чи є загальноприйняті вказівки щодо того, як писати сучасний C?


13

У мене сильний Java / Groovy фон, і я був призначений до команди, яка підтримує досить велику базу коду С для адміністративного програмного забезпечення.

Деякі больові моменти, як, наприклад, робота з кровотоком у базі даних або генерування звітів у форматі PDF та Excel, були екстерналізовані до веб-сервісу Java.

Однак, як розробник Java, мене трохи бентежать деякі аспекти коду:

  • це багатослівний (особливо якщо мова йде про "виняток")
  • є багато величезних методів (багато 2000+ рядків метод)
  • немає вдосконалених структур даних (я дуже сумую за списком, набором та картою)
  • немає розлуки (SQL радісно змішується навколо коду)

Як результат, я відчуваю, що бізнес прихований у тоннах технічного коду, і мій мозок, сформований з об'єктно-орієнтованим та дрібкою функціонального програмування, не є легким.

Хороша сторона проекту полягає в тому, що код прямо вперед: немає ні рамки, ні байтових маніпуляцій кодом під час виконання, ні AOP. І сервер може одночасно відповідати на 10000+ користувачів за допомогою однієї машини, використовуючи менше пам’яті, ніж потрібно Java, щоб виплюнути «привіт світ».

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

Щось схоже на еквівалент книги "Ефективна Java", але для C.

Редагуйте у світлі відповідей та коментарів:

  • Я спробую адаптувати своє мислення до коду С, а не намагатись віддзеркалити його до OOP.
  • Я почав сканувати та читати рекомендовані посібники стилю кодування з коментаря (Стандарти кодування GNU та Стиль кодування ядра Linux).
  • Тоді я спробую запропонувати цей стиль коду своїм колегам. Найскладнішою частиною може бути переконання колег, що величезний метод можна розділити на більш дрібні частини і що повторити ті самі 4 рядки коду обробки помилок можна уникнути за допомогою методу.

5
Чи потрібна додатка насправді модернізація, чи ви просто думаєте, що це робиться, оскільки спосіб написання незнайомий?
Blrfl


1
@Blrfl, я вважаю, що заявка написана із застарілим стандартом. Я просто хочу знати, що є сьогодні (2016) стандартом для (адміністративного) C. Якщо такий є. Я не хочу переробляти або переробляти поточний додаток, я хочу мати уявлення про те, як мені написати наступну частину коду.
Гійом


3
@antlersoft: Функція на 2000 ліній, яка складає довгий список простих речей одна за одною, абсолютно не проблема і не потребує виправдання. Будь ласка, не відповідайте циркулярними аргументами на кшталт "не слід писати 2000 лінійних функцій, оскільки ви не повинні записувати 2000 лінійних функцій".
gnasher729

Відповіді:


14

Я можу прочитати з вашого запитання, що проблема полягає не в тому, що код старий C, а просто поганому програмуванні. Більшість проблем, про які ви згадали, як багатослівність, величезні 2000-лінійні функції або відсутність роз'єднань, стосується будь-якої мови, як C, так і Java.

Верболітет згадувався в контексті поводження з помилками. Ви не надали приклад, тому можу лише нагадати, що код обробки помилок - це також код . Немає виправдання для повторюваних розділів кодового коду. Вирішити це; або до функції, або (якщо не варто створювати окрему функцію), зробіть goto Error;шаблон і перемістіть обробку помилок та очищення ресурсів до Error:розділу внизу функції.

Якщо передача помилки вгору по ланцюгу дзвінків здається проблемою, запитайте себе: чи справді потрібна функція там, щоб хтось маленький хлопець внизу мав проблему? Механізми винятку, вбудовані в мову, полегшують це, але в цілому краще обробляти винятки рано (будь-якою мовою), щоб умова помилки не забруднювала логіку коду високого рівня. І якщо функцію там дійсно потрібно знати, існують способи емуляції винятків за допомогою setjmpта і longjmp.

Я думаю, що єдиною згаданою проблемою, що стосується С, є відсутність стандартних контейнерів. Хоча Setвзагалі можна замінити відсортований масив і Map(здебільшого) масив пар або struct(якщо ви знаєте, що ключ встановлений перед рукою, map[key] = valueперетворюється на s.key = value), але факт полягає в тому, що в стандарті немає контейнера динамічного масиву бібліотека. У C99 ви можете принаймні оголосити масив змінної довжини на стеці ( int array[len]), але вам потрібно lenзаздалегідь обчислити (як правило, не важко), і, звичайно, ви не можете повернути його як будь-який об'єкт, виділений стеком. Більшість проектів закінчуються написанням власного контейнера динамічного масиву або прийняттям з відкритим кодом.

У заключній ноті я хочу зазначити, що я там був. Я був програмістом Java, який перейшов на C ++ та чистий C. Я б радив "прочитати книгу X, щоб навчитися хорошому С", але не існує жодного, як і для Java. Шлях вперед - просочити всі тонкощі мови та стандартної бібліотеки; google багато, багато читайте і багато кодуйте, поки не почнете думати в C. Спроба писати речі на C так, як на Java, так само неприємно, як намагатися написати речення іноземною мовою зі словами, безпосередньо перекладеними від вашої матері язик; і ви, і читач будуть скупитися. Хороша новина полягає в тому, що навчання хорошого програмування відбувається повільно, але вивчення іншої мови відбувається швидко. тому якщо ви пишете гідний код на Java,


1
Загалом, це дійсно гарна відповідь. Я б просто заперечував проти того, щоб бачити setjmp()/ longjmp()як дійсний інструмент: він навіть не намагається виконати будь-яку очистку. Будь-який розподіл буде просочено, будь-який тримається блокування не буде випущено, будь-який відкритий файл не буде закритий, а будь-яка перехідна невідповідність даних стане постійною. IMHO, ця функціональна пара - це в основному найгірший хакер, який коли-небудь був винайдений, з єдиним виправданням, що це вдалося реалізувати. Зрештою, дійсно існує лише один дійсний спосіб зробити помилку в C: явні коди помилок.
cmaster - відновити моніку

@cmaster так. Особисто setjmp/longjmpмені здається, що риба виходить із води в С, і я ніколи їх не вживав. Я змушений був включати їх лише через численні підручники / бібліотеки в Інтернеті для наслідування винятків, тому я подумав, що є люди, які насправді ними користуються.
Сова

7

Хороша сторона проекту полягає в тому, що код прямо вперед: немає ні рамки, ні байтових маніпуляцій кодом під час виконання, ні AOP. І сервер може одночасно відповідати на 10000+ користувачів за допомогою однієї машини, використовуючи менше пам’яті, ніж потрібно Java, щоб виплюнути «привіт світ».

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

Якщо ви все ще хочете спуститися цим маршрутом, я б запропонував таке:

  • Складіть (або генеруйте) діаграму стану програмного забезпечення / коду
  • Зануріться в код і складіть список найскладніших або найважливіших частин коду відповідно
  • Знайдіть когось, хто обізнаний з цією базою коду, і запитайте їх, чому він побудований таким чином і що, як відомо, створює проблеми
  • Напишіть документацію з того, що ви дізналися

У цей момент ви вирішите, чи варто це вивчити чи ні. Якщо культура вашої компанії не винагороджує провал, то отримайте зелене світло від вищого керівника чи менеджера.

  • Розділіть різні складові програми програмного забезпечення та напишіть одиничні тести для кожного.
  • Повторіть, поки ви не зможете склеїти різні модулі разом
  • Зробіть подальше тестування, яке імітує реальну взаємодію користувачів (стрес-тести тощо)

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


2
Я розумію, що моє питання зовсім не було зрозумілим. Я не хочу переробляти код. Зовсім. Я хочу підтримувати існуючу базу коду такою, якою вона є. Однак я хочу навчитися писати сучасний C для нової функції. І тут я загубився. Більшість документації, яку я знайшов, стосується того, як кодувати C, а не про те, як писати "сучасний" C. Можливо, немає такого поняття, як "сучасний" C ...
Гійом

1

Якщо ви віддаєте перевагу мові вищого рівня, є деякі мови на зразок C ++ або Objective-C, які можна легко змішувати з кодом C.

Альтернативно, C і C ++ є досить сумісними. Можливо, ви зможете просто скласти всю базу коду як C ++ з кількома змінами - у вас з'явиться випадкова змінна назва "клас" або "шаблон", яку потрібно перейменувати, але на практиці це буде все. (sizeof ('a') відрізняється в C і C ++, але я не думаю, що я ніколи цього не використовував).

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


1
Я маю тут не погодитися. C і C ++ - це різні мови, і якийсь код, необхідний компілятору C ++ (явно передаваючи зворотне значення malloc), вважається поганою практикою в C. Сенс constі inlineтакож сильно відрізняється між C і C ++, і звичайно C ++ не розуміє __restrict. Не ставтесь до мов як до взаємозамінних, навіть у підмножині джерел, які збираються в обох.
Ендже вже не пишається ТАК

1

В основному, написання хорошого коду С точно так само, як написання хорошого C ++ або Java-коду: Ви хочете клас, використовуйте struct. Ви хочете отримати спадщину, включіть базу structяк безіменного першого члена. Ви хочете віртуальних функцій, додайте покажчик до статичних structфункцій вказівників. І т.д. звикли.

Справа в тому, що добре програмування стосується парадигм, а не мовних особливостей. Правда, завжди приємно, якщо мовні функції забезпечують хорошу підтримку парадигм, які ви хочете використовувати, але мовні функції не є обов'язковим. Як тільки ви зрозумієте це, ви можете написати хороший код майже будь-якою мовою (крім деяких езотеричних мов, таких як brainfuck або INTERCAL, тобто).

Звичайно, проблема залишається в тому, що стандартна бібліотека C не містить жодного з тих чудових контейнерних класів, до яких ви звикли. На жаль, це означає, що вам потрібно буде або використовувати свій власний, або обходити цей недолік за допомогою динамічно розподілених масивів. Але я би ставлю, що незабаром ви дізнаєтесь, що все, що вам дійсно потрібно, - це динамічні масиви ( malloc()) та пов'язані списки / дерева, які реалізуються через членів вказівника у ваших класах.

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