Как Большой Должен основной () Быть, в C? [закрытый]

Я изучаю немного C за праздничные выходные, и я начал смотреть на другие программы, записанные в C. Я закончил тем, что смотрел на GNU Netcat, думая, что это будет хороший пример.

Я был немного потрясен видеть 600 строк main() функция. Действительно ли это нормально? Если нормально, что это считают хорошим C кодированием методов?

12
задан Joseph Kern 28 December 2009 в 00:04
поделиться

12 ответов

Есть цитата американского президента (Линкольна?), которого спросили, как долго должны быть ноги мужчины. "Достаточно длинными, чтобы дотянуться от тела до земли", - сказал он.

Возвращаясь к теме:

Авторы книг вроде "Чистого Кода" рекламируют, что каждая функция делает только одно (что я здесь грубо упрощаю), так что теоретически ваша main() должна вызывать функцию инициализации, а затем другую функцию, организующую работу приложения, и все.

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

Если программа сложна и большая часть ее функциональности находится в main(), то кто-то не сделал достойной попытки разобраться с проблемой. По сути, нужно стремиться к управляемости, понятности и читабельности. Обычно нет веских причин для того, чтобы main() была огромной.

.
22
ответ дан 2 December 2019 в 03:11
поделиться

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

1
ответ дан 2 December 2019 в 03:11
поделиться

В некоторых типах приложений я часто встречаю, что main() имеет сотни строк инициализации, за которыми следуют около 20 строк цикла верхнего уровня.

Это моя привычка - не разбивать функции, пока мне не понадобится вызвать их дважды. Иногда это приводит меня к написанию 300-строчной функции, но как только я вижу, что один и тот же блок возникает дважды, я вырываю этот блок.

Что касается main, то процедуры инициализации часто бывают один раз, так что 600 строк не кажутся неразумными.

.
7
ответ дан 2 December 2019 в 03:11
поделиться

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

600 строк звучит довольно длинно для любой реализации. Возможно, есть какая-то основная причина не передавать аргументы и ясность (я не смотрел на приведенный вами пример), но это звучит как дальний конец того, что обычно практикуется, и должна быть возможность разделить эту задачу.

Я подозреваю, что она разрабатывалась путем постоянного инкрементального добавления функциональности на протяжении многих лет, и никто не останавливался и не рефакторинговал ее, чтобы она была более читабельной/достижимой. Если для этого нет юнит-тестов (а по моему опыту main() методы не часто получают письменные тесты - по каким бы то ни было причинам) тогда будет понятное нежелание рефакторировать его.

4
ответ дан 2 December 2019 в 03:11
поделиться

Предел - окно редактора...

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

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

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

Но если это тестирование и разветвление и returning и breaking и continue- то его нужно разбить на отдельные и индивидуально тестируемые функциональные компоненты.

.
4
ответ дан 2 December 2019 в 03:11
поделиться

Надеюсь, они планируют рефакторинг. Это выглядит очень грубо.

  443   while (optind < argc) {
  444     const char *get_argv = argv[optind++];
  445     char *q, *parse = strdup(get_argv);
  446     int port_lo = 0, port_hi = 65535;
  447     nc_port_t port_tmp;
  448 
  449     if (!(q = strchr(parse, '-')))    /* simple number? */
  450       q = strchr(parse, ':');     /* try with the other separator */
  451 
  452     if (!q) {
  453       if (netcat_getport(&port_tmp, parse, 0))
  454   netcat_ports_insert(old_flag, port_tmp.num, port_tmp.num);
  455       else
  456   goto got_err;
  457     }
  458     else {        /* could be in the forms: N1-N2, -N2, N1- */
  459       *q++ = 0;
  460       if (*parse) {
  461   if (netcat_getport(&port_tmp, parse, 0))
  462     port_lo = port_tmp.num;
  463   else
  464     goto got_err;
  465       }
  466       if (*q) {
  467   if (netcat_getport(&port_tmp, q, 0))
  468     port_hi = port_tmp.num;
  469   else
  470     goto got_err;
  471       }
  472       if (!*parse && !*q)     /* don't accept the form '-' */
  473   goto got_err;
  474 
  475       netcat_ports_insert(old_flag, port_lo, port_hi);
  476     }
  477 
  478     free(parse);
  479     continue;
  480 
  481  got_err:
  482     free(parse);
  483     ncprint(NCPRINT_ERROR, _("Invalid port specification: %s"), get_argv);
  484     exit(EXIT_FAILURE);
  485   }
3
ответ дан 2 December 2019 в 03:11
поделиться

Главная линия 600 - это бит предупреждающего знака. Но если вы посмотрите на него и не увидите другого способа разбить его на более мелкие кусочки, кроме как сделать это.

void the_first_part_of_main(args...);
void the_second_part_of_main(args...);
...

main()
{
   the_first_part_of_main();
   the_second_part_of_main();
   ...
}

Тогда вам следует оставить его в покое.

3
ответ дан 2 December 2019 в 03:11
поделиться

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

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

.
2
ответ дан 2 December 2019 в 03:11
поделиться

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

.
1
ответ дан 2 December 2019 в 03:11
поделиться

Я бы сказал, что ваши процедуры должны быть настолько длинными/короткими, насколько это необходимо, чтобы быть эффективными, надежными и автоматически проверенными. Рутина с 600-ю степенями, скорее всего, имеет несколько путей через нее, и комбинации процедур могут очень быстро стать очень большими. Я пытаюсь разбить функции на что-то, что делает их легко читаемыми. Функции либо "функциональные", либо "повествовательные". Все это время включая юнит-тесты.

1
ответ дан 2 December 2019 в 03:11
поделиться

Почти все 600-строчные функции, которые я видел, тоже были написаны глупо. Это не обязательно должно быть так.

Однако в этих случаях программист просто не смог представить какой-то увеличенный вид и дать значимые имена секциям - как высокоуровневым (например, Initialize()), так и низкоуровневым (что-то, что берет общий 3-строчный паттерн и скрывает его под одним именем, с параметрами).

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

.
1
ответ дан 2 December 2019 в 03:11
поделиться

main(), как и любая другая функция, должна быть точно такой же большой, какой она должна быть. "Как и должно быть" будет сильно варьироваться в зависимости от того, что ей нужно делать. Сказав это, она не должна иметь больше, чем пара сотен строк в большинстве случаев. 600 строк немного сложнее, и некоторые из них могут/должны быть рефакторизованы в отдельные функции.

Для крайнего примера, одной команде, в которой я работал, было поручено ускорить некоторый код для управления 3d дисплеем. Первоначально код был написан прослушивающим устройством, которое, очевидно, обучалось программированию с использованием FORTRAN старой школы; main() было больше пяти тысяч строк кода, со случайными битами #include ed здесь и там. Вместо того, чтобы разбивать код на функции, он просто разветвлялся на подпрограмму в пределах main() через goto (где-то между 13 и 15 готами, разветвляя оба направления, казалось бы, случайным образом). В качестве первого шага мы просто включили оптимизацию 1-го уровня, компилятор быстро поглотил всю доступную память и пространство подкачки и запаниковал кернел. Код был настолько хрупким, что мы не смогли ничего изменить , не взломав. В конце концов, мы сказали клиенту, что у него есть два варианта: позволить нам переписать всю систему с нуля или купить более быструю аппаратуру.

Они купили более быстрое оборудование.

0
ответ дан 2 December 2019 в 03:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: