Подтвердить что ты не робот

Как большой должен быть главный() Быть, в C?

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

Хех, это ужасно, но я видел хуже. Я видел большие многомиллионные программы fortran без каких-либо подпрограмм.

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

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

Но если это тестирование и ветвление и return ing и break ing и continue -ing, то его необходимо разбить на индивидуальные и индивидуально протестированные функциональные компоненты.

Ответ 5

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

  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   }

Ответ 6

Основная линия 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();
   ...
}

Тогда вы должны оставить его в покое.

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

Ответ 11

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

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

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

Ответ 12

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

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

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