Существуют ли общепринятые рекомендации о том, как писать современный C?

13

У меня сильный опыт работы с Java / Groovy, и я был назначен в команду, которая поддерживает довольно большую базу кода C для административного программного обеспечения.

Некоторые болевые точки, такие как работа с BLOB-объектами в базе данных или создание отчетов в PDF и Excel, были перенесены в веб-службу Java.

Однако, как разработчик Java, я немного смущен некоторыми аспектами кода:

  • это многословно (особенно когда речь идет об «исключении»)
  • есть много огромных методов (метод с более чем 2000 строк)
  • нет сложных структур данных (я очень скучаю по List, Set и Map)
  • нет разделения на проблемы (SQL радостно смешал весь код)

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

Хорошая сторона проекта в том, что код прост: нет фреймворка, нет манипуляций с байт-кодом во время выполнения, нет AOP. И сервер может одновременно отвечать более чем 10000 пользователям на одной машине, используя меньше памяти, чем нужно Java, чтобы выплюнуть «привет мир».

Я хочу научиться писать код на C в соответствии с общепринятыми современными принципами. Существуют ли общепринятые принципы того, как современный С должен быть написан и структурирован?

Нечто похожее на книгу «Эффективная Java», но для C.

Изменить с учетом ответов и комментариев:

  • Я постараюсь адаптировать свое мышление к коду C, а не пытаться отразить его в ООП.
  • Я начал сканировать и прочитать рекомендуемые руководства по стилю кодирования из комментария (Стандарты кодирования GNU и Стиль кодирования ядра Linux).
  • Затем я попытаюсь предложить этот стиль кода моим коллегам. Самым сложным может быть убедить коллег в том, что огромный метод можно разбить на более мелкие части и что с помощью метода можно избежать повторения тех же четырех строк кода обработки ошибок.
Гийом
источник
5
Нужно ли модернизировать приложение или вы просто думаете, что это так, потому что то, как оно было написано, незнакомо?
Blrfl
1
@Blrfl, я чувствую, что приложение было написано с устаревшим стандартом. Я просто хочу знать, что сегодня (2016) стандарт для (административного) C. Если есть. Я не хочу реорганизовывать или изменять текущее приложение, я хочу иметь представление о том, как мне написать следующую часть кода.
Гийом
3
@antlersoft: функция из 2000 строк, которая выполняет длинный список простых вещей, одна за другой, абсолютно не проблема и не нуждается в оправдании. Пожалуйста, не отвечайте круглыми аргументами, такими как «вы не должны писать 2000 строковых функций, потому что вы не должны писать 2000 строковых функций».
gnasher729

Ответы:

14

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

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

Если проходя ошибку вверх по цепочке вызовов , как представляется, проблема, спросите себя: это функция там действительно нужно знать какой - нибудь парень , здесь была проблема? Механизмы исключений, встроенные в язык, упрощают это, но в целом лучше обрабатывать исключения рано (на любом языке), чтобы условие ошибки не загрязняло логику высокоуровневого кода. И если функция действительно должна знать, есть способы эмулировать исключения с помощью setjmpи longjmp.

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

В заключительной записке я хотел бы отметить, что я был там. Я был программистом на Java, который перешел на C ++ и чистый C. Я хотел бы посоветовать «прочитай книгу X, чтобы выучить хороший C», но не существует ничего подобного для Java. Способ продвинуться вперед - впитать все тонкости языка и стандартной библиотеки; Google много, много читал, и код много , пока вы не начнете думать в С. Попытка писать вещи в C , как вы бы в Java как разочаровывает , как пытаются написать фразу на иностранном языке со словами непосредственно в переводе с вашей матери язык; и вы, и читатель съежитесь. Хорошая новость заключается в том, что обучение хорошему программированию происходит медленно, а изучение другого языка - быстро. так что если вы пишете достойный код на Java,

Сова
источник
1
В общем, это действительно хороший ответ. Я просто возражаю против того, чтобы рассматривать setjmp()/ longjmp()как действительный инструмент: он даже не пытается выполнить какую-либо очистку. Любое распределение будет утечкой, любая удержанная блокировка не будет снята, любой открытый файл не будет закрыт, и любое временное несоответствие данных станет постоянным. ИМХО, эта пара функций - в основном худший взлом, когда-либо изобретенный, с единственным оправданием, что было возможно осуществить это. В конце концов, в Си есть только один действительный способ обработки ошибок: явные коды ошибок.
cmaster - восстановить монику
@ мастер да. Лично setjmp/longjmpмне кажется, что рыба из воды в C, и я никогда не использовал их. Я чувствовал себя обязанным включать их только из-за многочисленных учебных пособий / библиотек в Интернете для имитации исключений, поэтому я подумал, что есть люди, которые действительно используют его.
Сова
7

Хорошая сторона проекта в том, что код прост: нет фреймворка, нет манипуляций с байт-кодом во время выполнения, нет AOP. И сервер может одновременно отвечать более чем 10000 пользователям на одной машине, используя меньше памяти, чем нужно Java, чтобы выплюнуть «привет мир».

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

Если вы все еще хотите пойти по этому пути, я бы предложил следующее:

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

На этом этапе вы решите, стоит ли это исследовать. Если культура вашей компании не вознаграждает за провал, тогда получите зеленый свет от высшего руководства или менеджера.

  • Разделите на части различные строительные блоки программного обеспечения и напишите для каждого модульные тесты.
  • Итерируйте, пока не сможете склеить разные модули
  • Проведите дальнейшее тестирование, которое имитирует реальное взаимодействие с пользователем (стресс-тесты и т. Д.)

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

Джерри Дин
источник
2
Я понимаю, что мой вопрос не был ясен вообще. Я не хочу рефакторинг кода. Совсем. Я хочу сохранить существующую кодовую базу такой, какая она есть. Однако я хочу научиться писать современный C для новой функции. И вот я потерян. Большая часть документации, которую я нашел, о том, как кодировать на C, а не о том, как писать «современный» C. Может быть, не существует такого понятия, как «современный» C ...
Гийом,
1

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

Альтернативно, C и C ++ достаточно совместимы. Возможно, вы сможете просто скомпилировать всю кодовую базу как C ++ с небольшими изменениями - у вас будет случайная переменная с именем «class» или «template», которую нужно переименовать, но на практике это будет все. (sizeof ('a') отличается в C и C ++, но я не думаю, что когда-либо использовал это).

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

gnasher729
источник
1
Я должен не согласиться здесь. C и C ++ являются различными языками, а некоторый требуется код на C ++ компилятор (явно литья возвращаемого значения malloc) считаются плохой практикой в C. Смысла constи inlineтакже очень отличается от C и C ++, и, конечно , C ++ не понимает __restrict, Не рассматривайте языки как взаимозаменяемые, даже в подмножестве источников, которые компилируются в обоих.
Angew больше не гордится SO
1

По сути, написание хорошего кода на C - это то же самое, что написание хорошего кода на C ++ или Java: вы хотите класс, используйте a struct. Вы хотите наследования, включите базу structв качестве безымянного первого члена. Вы хотите виртуальные функции, добавить указатель на статические structуказатели функций. И т. Д., И т. Д. Это именно то, что C ++ делает под капотом, единственное отличие состоит в том, что это явно на C. Таким образом, вы можете делать идеально объектно-ориентированное программирование на C, оно просто выглядит немного иначе и более наглядно, чем то, что вы делаете. привыкли к.

Дело в том, что хорошее программирование - это парадигмы, а не языковые особенности. Правда, всегда хорошо, если ваши языковые функции обеспечивают хорошую поддержку парадигм, которые вы хотите использовать, но языковые функции не являются обязательными. Как только вы поймете это, вы сможете написать хороший код практически на любом языке (кроме некоторых эзотерических языков, таких как brainfuck или INTERCAL).

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

cmaster - восстановить монику
источник