Есть ли инструмент анализа кода, который может уменьшить количество условных ветвлений?

Я хочу уменьшить глубокое вложенное ветвление этого кода C, и мне интересно, есть ли инструмент анализа, который может составить таблицу истинности для условий, или мне придется анализировать ее вручную? Я хотел бы сделать код более читабельным и менее разветвленным. В моей IDE CLion от JetBrains ничего не сказано о том, как выполнить такой рефакторинг. Можно ли его автоматизировать? Я не пробовал использовать Lint, но я мог бы попробовать.

if (ptr + j) {
    if (*(ptr + j)[0] == '{') {
        keep = true;
    }
    if (testFn(*(ptr + j))) { /* test for last char */
        string[i][j - p] = concat(*pString1, *(ptr + j));
        keep = false;
        free(*pString1);
        goto mylabel;
    }
    if (keep) {
        *pString1 = concat(*pString1, *(ptr + j));
        *pString1 = concat(*pString1, " ");
        p++;
    } else {
        b1 = false;
        int q = j;
        for (e = 0; *(ptr + q + e); e++) { /* step through the string */
            b1 = true;
            if (*(ptr + e + q)) {
                *pString = concat(*pString, *(ptr + e + q));
                *pString = concat(*pString, " ");
            }
            j = e;
        }
        if (makeArgs(*pString, &argc, (const char ***) &argv, pipe, i, h)) {
            write_command(&w, argv, string[w]);
            w++;

        } else {
            if (!b1) { /* no args (?) */
                for (int r = 0; argv[r] != NULL; r++) {
                    string[i][r] = argv[r]; /* is this necessary? */
                }
            }
        }
    }
}

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

Полная функция сегодня выглядит так:

static int runCmd(const char *cmd) {
    const char *cp;
    pid_t pid;
    int status;
    struct command structcommand[15];
    char **argv = 0;
    int argc = 1;
    bool pipe = false;
    char *string[z][z];
    char *pString3[40];
    char *pString2[40];
    int n = 0;
    char **ptr1;
    char string1[z];
    bool keep = false;
    char *pString1[z];
    char *pString[z];
    *pString1 = "\0";
    *pString = "\0";
    char *temp = {'\0'};
    int w = 0;
    bool quote = false;
    int rrs[256];
    int j = 0;
    int i;
    int p = 0;
    char **ptr;
    int count = 0;
    char *cmdtmp;
    bool b1 = false;
    int y = 0;
    i = 0;
    int h = 0;
    char *str;
    char *freeme[75][75];
    char **dealloc[75];
    char **dealloca[75][75];
    int acount[128];
    nullterminate(string);
    int rr = 0;
    for (z = 0; z < 128; z++) {
        acount[z] = -1;

    }
    for (int f = 0; f < 75; f++) {
        dealloc[f] = NULL;
        for (z = 0; z < 75; z++) {
            freeme[f][z] = NULL;
        }
    }
    if (cmd) {
        for (cp = cmd; *cp; cp++) {
            if ((*cp >= 'a') && (*cp <= 'z')) {
                continue;
            }
            if ((*cp >= 'A') && (*cp <= 'Z')) {
                continue;
            }
            if (isDecimal(*cp)) {
                continue;
            }
            if (isBlank(*cp)) {
                continue;
            }
            if ((*cp == '.') || (*cp == '/') || (*cp == '-') ||
                (*cp == '+') || (*cp == '=') || (*cp == '_') ||
                (*cp == ':') || (*cp == ',') || (*cp == '\'') ||
                (*cp == '"')) {
                continue;
            }
        }
        cmdtmp = strdup(cmd);
        ptr1 = str_split(pString3, cmdtmp, '|');
        if (strstr(cmd, "|") == NULL) {         /* not a pipeline */
            makeArgs(cmd, &argc, (const char ***) &argv, pipe, 0, 0);
            write_argument(&argc, structcommand, argv, string[0]);
            n++;
        }
        else {
            for (i = 0; *(ptr1 + i); i++) { /* loop for each pipeline*/
                n++;
                /* save number of pipelines */
                dealloc[n] = NULL;
                int e = 0; /* a counter */
                *pString = "\0"; /* should malloc and free this? */
                strcpy(string1, *(ptr1 + i));
                if ((string1[0] != '\0') &&
                    !isspace(string1[0])) { /* this is neither the end nor a new argument */ /* BSD bug? check*/

                    ptr = str_split(pString2, *(&string1), ' '); /* split the string at the arguments */
                    dealloc[rr] = ptr;
                    rr++;
                    h = 0;
                    for (j = 0; *(ptr + j); j++) { /* step through the arguments */
                        dealloca[n][n - 1] = NULL;
                        /* the pipeline is in cmdtmp and the argument/program is in ptr[i] */
                        if (ptr + j && !quote && strstr(*(ptr + j), "'")) { /* is quote? */
                            quote = true;
                            strcpy(temp, *(ptr + j)); /* point where quoted piipelines crash */
                            if (y < 1) {
                                y++;
                            }
                        }
                        while (quote) {
                            if (*(ptr + j) && strstr(*(ptr + j), "'")) { /* end of quote */
                                quote = false;
                                if (y < 1) {
                                    string[i][j] = strcpy(temp, *(ptr + j));
                                }
                                y = 0;
                            }
                            else if (*(ptr + j)) { /* read until end of quote */
                                string[i][j] = temp;
                                continue;
                            } else {
                                quote = false;
                                break;
                            }
                        }
                        if (ptr + j) { ;
                            if (*(ptr + j)[0] == '{') {
                                keep = true;
                            }
                            if (testFn(*(ptr + j))) { /* test for last char */
                                string[i][j - p] = concat(*pString1, *(ptr + j));
                                keep = false;
                                free(*pString1);
                                continue;//goto mylabel;
                            }
                            if (keep) {
                                str = concat(*pString1, *(ptr + j));
                                *pString1 = concat(str, " ");
                                free(str);
                                p++;
                            } else {
                                b1 = false;
                                int q = j;
                                freeme[i][0] = *pString;
                                for (e = 0; *(ptr + q + e); e++) { /* step through the string */
                                    b1 = true;
                                    if (*(ptr + e + q)) {
                                        str = concat(*pString, *(ptr + e + q));

                                        *pString = concat(str, " "); /* how to free() ? */
                                        free(str);
                                        freeme[i][e] = *pString;



                                    }
                                    j = e; /* adjust the counter */
                                }


                                if (makeArgs(freeme[i][e - 1], &argc, (const char ***) &argv, pipe, i, h)) {
                                    write_command(&w, argv, string[w]);
                                    w++;
                                    for (int qwe = 0; qwe < argc; qwe++) {

                                        dealloca[n - 1][qwe] = &argv[qwe];
                                    }
                                    acount[n - 1] = argc;

                                } else {
                                    if (!b1) { /* no args (?) */
                                        for (int r = 0; argv[r] != NULL; r++) {
                                            string[i][r] = argv[r]; /* is this necessary? */
                                        }
                                    }
                                }
                            }
                        }

                    }

                    bool boo = false;
                    dump_argv((const char *) "d", argc, argv, boo);
                }



            }


        }

        for (i = 0; i < n; i++) {

            structcommand[i].argv = string[i];
            for (j = 0; string[i][j] != NULL; j++) {
                if (string[i] != NULL) {


                }
            }



        }
        free(cmdtmp);
        if (ptr1) {
            int i;
            for (i = 0; *(ptr1 + i); i++) {

                free(*(ptr1 + i));
            }
            printf("\n");
            free(ptr1);
        }

        fflush(NULL);
        pid = fork();
        if (pid < 0) {
            perror("fork failed");
            return -1;
        }
        /* If we are the child process, then go execute the string.*/
        if (pid == 0) {
            /* spawn(cmd);*/
            fork_pipes(n, structcommand);
        }
        /*
         * We are the parent process.
         * Wait for the child to complete.
         */
        status = 0;
        while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR));
        if (pid < 0) {
            fprintf(stderr, "Error from waitpid: %s", strerror(errno));
            return -1;
        }
        if (WIFSIGNALED(status)) {
            fprintf(stderr, "pid %ld: killed by signal %d\n",
                    (long) pid, WTERMSIG(status));

            return -1;
        }
    }


    for (i = 0; i < n; i++) {

        for (j = 0; string[i][j] != NULL; j++) {
            if (string[i] != NULL) {

                if (string[i][j])
                    free(string[i][j]);

            }
        }

    }

    int z;
    for (int f = 0; f < n; f++) {
        if (f > 0) {

        }
        for (z = 0; freeme[f][z]; z++) {

            free(freeme[f][z]);
        }

    }

    size_t idx;
    for (int f = 0; n > 1 && f < n; f++) {

        for (idx = 0; *(dealloc[f] + idx) != NULL; idx++) {
            free(*(dealloc[f] + idx));
        }
        free(dealloc[f]);

    }

    return WEXITSTATUS(status);


}

Код сканирует и анализирует другую программу, поэтому так много манипуляций со строками, сохранения и просмотра символов и указателей.

Если ваш код все еще используется goto- значит, избегая использования keep, вы не преуспели.
@SteveBarnes Я должен быть в состоянии переписать, gotoно мне это действительно нравится, потому что это так редко. Мы можем сделать это с помощью «break , continue», что мне также нравится больше, чем переменные. На самом деле, если вы программируете на ассемблере, вы делаете это gotoчасто.
Да, это важно в ассемблере, но C считается очень плохой практикой из-за проблем с а) проблемами обслуживания и б) повреждением стека, потому что это слишком просто gotoгде-то за пределами функции или процедуры - не возвращаться. Вот почему это редкость!
Мой проект ок. 2000 строк кода, и я использовал gotoтолько один раз, потому что был ленив. Я обещаю, что поменяю его на a breakили a continue, но мне не нравится boolean...
Похоже, ваш код проверяет неинициализированное значение keep, если ваши первые два (вложенных) условных выражения оцениваются как ложные. Либо это ошибка, либо вы не показали нам весь соответствующий код.
@IraBaxter Да, это выглядит так, но оно инициализировано. В основном меня беспокоит правильность, удобочитаемость и ремонтопригодность, поэтому я использую инструмент Valgrind для устранения всех своих ошибок и утечек памяти. Даже если программа работает «идеально», в ней могут быть утечки памяти, которые не проявляются до тех пор, пока анализ не обнаружит ошибки.
Что именно вы подразумеваете под «таблицей истинности»? Хотите знать полное условие, при котором выполняется каждый блок кода? (например, для части then "if (keep)" полным условием будет "*(ptr + j)[0] == '{' && !testFn(*(ptr + j)):? Вы должны обновите свой вопрос, чтобы сделать то, что вы хотите для этого явным.
Этот код очень странный. Я предполагаю, что «ptr» объявлен как «char *». Как может "ptr+j" быть равным нулю? Можете ли вы объяснить, что делает "*(ptr+j)[0]"? Он действительно компилируется?
@IraBaxter Обычно это делается с помощью «абстрактного синтаксического дерева» с алгоритмом парсера рекурсивного спуска, но я сделал это с помощью циклов и матрицы, представляющей программу, где первая строка — это первая трубка, а первый столбец первой строки — первый аргумент первой трубы. *(ptr + j)[0] на самом деле очень конкретный текущий символ того, что сканируется, ptrявляется началом канала, а j - смещением. 0 означает первый символ аргумента. Спецификация [здесь] (pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html).
Итак, "ptr" объявлен как "**char". Как может быть "ptr+j" равным нулю? Вы не ответили на вопрос о том, что вы явно хотите для «таблиц истинности». (Действительно ли все эти размеры массивов запрограммированы? Это не предварительно обработанный код, он все еще содержит комментарии). (Да, я знаю о парсерах рекурсивного спуска, но я обычно не реализую их так: см. stackoverflow.com/questions/2245962/… )
Я мог бы сделать это с помощью рекурсии (которая используется для вызова forkи exec, но циклы для меня быстрее и проще кодировать, чем рекурсия. Я думаю, что идея хороша, чтобы сделать таблицу истинности для условий, чтобы увидеть, всегда ли что-то или никогда не верно, потому что я пока не знаком с этим подробным уровнем указателя C, но я работаю над этим. Здесь вы можете найти весь репозиторий для этой вещи, которая является моей собственной оболочкой, похожей на sashили dash, Интересно, что Valgrind может найти так много об оперативной памяти, я написал тест, который использует Valgrind.
Не автоматизированный инструмент, а стандартный способ написания программ без перехода без использования дополнительных переменных: stackoverflow.com/a/36661381/120163
Анализ не может изменить ваш код. Анализ — это операция только для чтения. Возможно, сначала стоит ознакомиться с некоторыми понятиями, например, pultsight.com/courses/brownfield . Напишите модульные тесты, измерьте покрытие кода или лучшее покрытие ветвей. Затем найдите IDE, в которой есть методы рефакторинга, такие как «метод извлечения» и «инвертировать оператор if», что-то вроде Jetbrains Resharper, но для C. Она выявляет логические ошибки, такие как «Это условие всегда верно».
Перепишите это с помощью классов (С++) и полиморфизма и избегайте ветвления. stackoverflow.com/questions/519515/…
Вы можете рассмотреть возможность простой реорганизации кода, чтобы сделать его читабельным. См. Stackoverflow.com/q/37079307/120163 .
@ИраБакстер Да. Моя IDE сообщает мне, когда условие «всегда верно», и тогда я могу его удалить. Сегодня я изучаю, как сделать грамматику, но я застрял на том, как реализовать правила. Я пробую это с парсером лимона для whileключевого слова expr(A)::= WHILE LPAR expr(B) RPAR expr(C). {printf("test"); }, но тестовая строка не печатается. Мой код запутался, и вместо этого я пытаюсь сделать грамматику.
Вы действительно, действительно не хотите создавать свой собственный синтаксический анализатор C. Во-первых, это намного сложнее, чем кажется, потому что C намного сложнее, чем вы думаете, компиляторы не согласны с тем, что допустимо, и вам нужно правильно настроить препроцессор. Если вам удастся сделать все это, ваша следующая проблема заключается в том, что синтаксический анализатор недостаточно хорош, чтобы с ним что-то делать. См. мое эссе на сайте semanticdesigns.com/Products/DMS/LifeAfterParsing.html .
@IraBaxter Спасибо, Ира! Я добавил ссылки в закладки. Но я должен научиться писать грамматику. Я изучаю синтаксический анализатор лимона и почти могу подобрать whileключевое слово на основе простого калькулятора. Сегодня на обзоре кода они говорят, что мой код показывает «много» улучшений: codereview.stackexchange.com/questions/128149/…

Ответы (1)

Я добился определенного успеха, используя инструмент под названием CppCheck через систему Jenkins CI. Я специально не отслеживаю условные ветки, но количество проверок , которые предоставляет этот инструмент, того стоит. В частности, проверьте Conditionчасть, в которой перечислены различные проверки условий, которые всегда являются истинными/ложными (включая отслеживание значений, повторяющиеся условия, логику интервалов), но в других категориях также перечислены некоторые, возможно, полезные проверки, такие как:

  • «добавление указателя в условии»
  • «одинаковый код в обеих ветвях if/else или тернарного оператора».
  • другие типы подозрительных или избыточных условий (для STL, строк, логических/числовых операций, ...)

И он доступен в виде плагина для вашей IDE.

«одинаковый код в обеих ветвях if/else или тернарного оператора». Ух ты. Это самый интересный пример того, что может сделать CppCheck? Я занимаюсь кодированием 45 лет и никогда не сталкивался с этим.