Gibt es ein Codeanalysetool, das meine bedingte Verzweigung reduzieren kann?

Ich möchte die tief verschachtelte Verzweigung dieses C-Codes reduzieren und frage mich, ob es ein Analysetool gibt, das eine Wahrheitstabelle für die Bedingungen erstellen kann, oder ob ich es manuell analysieren muss? Ich möchte den Code lesbarer und weniger Verzweigungen machen. Meine IDE CLion von JetBrains sagt nichts darüber aus, wie man ein solches Refactoring durchführt. Kann es automatisiert werden? Ich habe nicht versucht, Lint zu verwenden, aber ich könnte es versuchen.

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? */
                }
            }
        }
    }
}

Ich konnte die bedingte Verzweigung manuell reduzieren, aber ich habe keine Wahrheitstabelle erstellt. Ich denke, die Codeanalysen sollten mit einer Wahrheitstabelle sagen, welche Zweige wann identisch sind.

Die komplette Funktion sieht heute so aus:

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);


}

Der Code scannt und analysiert ein anderes Programm, deshalb ist es so viel String-Manipulation, Speichern und Vorausschauen auf Zeichen und Zeiger.

Wenn Ihr Code immer noch verwendet wird goto- also die Verwendung von keep vermeiden, dann haben Sie nicht gut getan.
@SteveBarnes Ich sollte in der Lage sein, das umzuschreiben, gotoaber ich mag es eigentlich, weil es so selten ist. Wir können es mit 'break Continue' machen, , das gefällt mir auch besser als Variablen. In der Tat, wenn Sie in Assembler programmieren, tun Sie dies gotooft.
Ja, es ist in Assembler unerlässlich, aber C wird aufgrund der Probleme mit a) Wartungsproblemen und b) Stapelbeschädigung als sehrgoto schlechte Praxis angesehen, da es zu einfach ist , irgendwo außerhalb der Funktion oder Prozedur zu landen - nicht zurückzukehren. Deshalb ist es selten!
Mein Projekt ist ca. 2000 Zeilen Code und ich habe gotonur einmal gebraucht, weil ich faul war. Ich verspreche, ich werde es in a breakoder a ändern continue, aber ich mag es nicht boolean...
Ihr Code scheint einen nicht initialisierten Wert von keep zu untersuchen, wenn Ihre ersten beiden (verschachtelten) Bedingungen beide als falsch ausgewertet werden. Das ist entweder ein Fehler oder Sie haben uns nicht den gesamten relevanten Code gezeigt.
@IraBaxter Ja, es sieht so aus, aber es ist initialisiert. Ich mache mir hauptsächlich Sorgen um Korrektheit, Lesbarkeit und Wartbarkeit, also verwende ich das Tool Valgrind, um alle meine Fehler und Speicherlecks zu beheben. Selbst wenn das Programm „perfekt“ läuft, kann es Speicherlecks geben, die erst auftauchen, wenn eine Analyse die Fehler entdeckt.
Was genau meinst du mit "Wahrheitstabelle"? Möchten Sie wissen, unter welcher vollständigen Bedingung jeder Codeblock ausgeführt wird? (z. B. für den then-Teil von „if (keep)“ wäre die vollständige Bedingung „*(ptr + j)[0] == '{' && !testFn(*(ptr + j)):? Das sollten Sie Aktualisieren Sie Ihre Frage, um das, was Sie dafür wollen, explizit zu machen.
Dieser Code ist sehr seltsam. Ich gehe davon aus, dass "ptr" als "char *" deklariert ist. Wie kann "ptr+j" jemals Null sein? Können Sie erklären, was "*(ptr+j)[0]" bewirkt? Kompiliert es tatsächlich?
@IraBaxter Normalerweise geschieht dies mit einem "abstrakten Syntaxbaum" mit einem rekursiven Abstiegs-Parser-Algorithmus, aber ich habe es mit Schleifen und einer Matrix gemacht, die ein Programm darstellt, bei dem die erste Zeile die erste Pipe und die erste Spalte der ersten Zeile ist das erste Argument der ersten Pipe. *(ptr + j)[0] ist eigentlich sehr konkret der aktuelle Charakter dessen, was gescannt wird, ptrist der Anfang der Pipe und j ist der Offset. 0 bedeutet erstes Zeichen des Arguments. Spezifikation [hier](pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html).
OK, also wird "ptr" als "**char" deklariert. Wie kann "ptr+j" jemals Null sein? Sie haben die Frage, was Sie explizit für "Wahrheitstabellen" wollen, nicht beantwortet. (Sind all diese Array-Größen wirklich fest verdrahtet? Dies ist kein vorverarbeiteter Code; er enthält immer noch Kommentare). (Ja, ich kenne rekursive Abstiegsparser, aber ich implementiere sie normalerweise nicht so: siehe stackoverflow.com/questions/2245962/… )
Ich könnte es vielleicht mit Rekursion machen (die zum Aufrufen forkvon und verwendet execwird, aber Schleifen sind für mich schneller und einfacher zu codieren als Rekursion. Ich denke, die Idee ist gut, eine Wahrheitstabelle für die Bedingungen zu erstellen, um zu sehen, ob etwas immer ist or never true, weil ich auf dieser detaillierten Ebene von C-Zeigern bisher keine Erfahrung habe, aber ich arbeite daran. Sie können das gesamte Repository für dieses Ding finden, das meine eigene Shell ähnlich sashoder ist dash. Es ist interessant, dass Valgrind das kann finde so viel über den Arbeitsspeicher, dass ich einen Test geschrieben habe, der Valgrind verwendet.
Kein automatisiertes Tool, sondern eine Standardmethode zum Schreiben von goto-freien Programmen ohne Verwendung zusätzlicher Variablen: stackoverflow.com/a/36661381/120163
Analysis kann Ihren Code nicht ändern. Die Analyse ist ein schreibgeschützter Vorgang. Machen Sie sich vielleicht zuerst mit einigen Konzepten vertraut, z. B. pluralsight.com/courses/brownfield Schreiben Sie Unit-Tests, messen Sie die Codeabdeckung oder verbessern Sie die Zweigabdeckung. Suchen Sie dann nach einer IDE, die Refactoring-Methoden wie "Extract-Methode" und "Invert if-Anweisung" hat, so etwas wie Jetbrains Resharper, aber für C. Es identifiziert logische Fehler wie "This if condition is always true".
Schreiben Sie dies mit Klassen (c++) und Polymorphismus um und vermeiden Sie Verzweigungen. stackoverflow.com/questions/519515/…
Sie könnten erwägen, den Code einfach neu zu organisieren, um ihn lesbar zu machen. Siehe stackoverflow.com/q/37079307/120163
@IraBaxter Ja. Meine IDE sagt mir, wenn eine Bedingung "immer wahr" ist und ich sie dann entfernen könnte. Heute lerne ich, wie man eine Grammatik erstellt, aber ich stecke bei der Umsetzung von Regeln fest. Ich versuche dies mit dem Lemon-Parser für ein whileSchlüsselwort expr(A)::= WHILE LPAR expr(B) RPAR expr(C). {printf("test"); }, aber die Testzeichenfolge wird nicht gedruckt. Mein Code wurde unordentlich und ich versuche stattdessen, eine Grammatik zu erstellen.
Sie wollen wirklich, wirklich keinen eigenen C-Parser bauen. Erstens ist es viel schwieriger, als es aussieht, weil C viel chaotischer ist, als Sie denken, die Compiler sind sich nicht einig, was legal ist, und Sie müssen den Präprozessor richtig machen. Wenn Sie das alles schaffen, besteht das nächste Problem darin, dass ein Parser einfach nicht gut genug ist, um damit viel zu tun. Siehe meinen Aufsatz auf semanticdesigns.com/Products/DMS/LifeAfterParsing.html
@IraBaxter Danke Ira! Ich habe die Links mit einem Lesezeichen versehen. Aber ich muss lernen, Grammatik zu schreiben. Ich lerne den Lemon-Parser und kann das whileSchlüsselwort basierend auf dem einfachen Taschenrechner fast ausführen. Jetzt sagen sie heute bei der Codeüberprüfung, dass mein Code "viel" Verbesserung zeigt: codereview.stackexchange.com/questions/128149/…

Antworten (1)

Ich hatte einige Erfolge mit einem Tool namens CppCheck über ein Jenkins CI-System. Ich verfolge bedingte Verzweigungen nicht speziell, aber die Anzahl der Überprüfungen , die dieses Tool bietet, ist einen Blick wert. Überprüfen Sie insbesondere den ConditionTeil, der verschiedene Überprüfungen auf Bedingungen auflistet, die immer wahr/falsch sind (einschließlich Wertverfolgung, doppelte Bedingungen, Intervalllogik), aber andere Kategorien führen auch einige möglicherweise nützliche Überprüfungen auf, wie:

  • „Zeigerzusatz im Zustand“
  • „identischer Code in beiden Zweigen von if/else oder ternären Operator.“
  • andere Arten von verdächtigen oder redundanten Bedingungen (für STL, Strings, logische/numerische Operationen, ...)

Und es ist als Plugin für Ihre IDE verfügbar.

„identischer Code in beiden Zweigen von if/else oder ternären Operator.“ Wow. Das ist das interessanteste Beispiel dafür, was CppCheck kann? Ich codiere seit 45 Jahren und bin so etwas noch nie begegnet.