¿Existe una herramienta de análisis de código que pueda reducir mi bifurcación condicional?

Quiero reducir la bifurcación anidada profunda de este código C y me pregunto si hay una herramienta de análisis que pueda hacer una tabla de verdad para las condiciones o si debo analizarla manualmente. Me gustaría hacer que el código sea más legible y menos ramificado. Mi IDE CLion de JetBrains no dice nada sobre cómo realizar dicha refactorización. ¿Se puede automatizar? No intenté usar Lint pero podría intentarlo.

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

Pude reducir la bifurcación condicional manualmente, pero no hice una tabla de verdad. Creo que los análisis de código deberían decir qué ramas son idénticas y cuándo con una tabla de verdad.

La función completa se ve así hoy:

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


}

El código está escaneando y analizando otro programa, por eso es tanta la manipulación de cadenas, guardar y mirar hacia adelante en los caracteres y punteros.

Si su código aún se usa goto, por lo que evita el uso de mantener, entonces no lo ha hecho bien.
@SteveBarnes Debería poder reescribirlo, gotopero en realidad me gusta porque es muy raro. Podemos hacerlo con 'break , continue' que también me gusta más que las variables. De hecho, si programa en ensamblador lo hace gotoa menudo.
Sí, es esencial en el ensamblador, pero se considera una muy mala práctica en C debido a los problemas con a) problemas de mantenimiento y b) corrupción de la pila porque es demasiado fácil ir a gotoalgún lugar fuera de la función o procedimiento, no regresar. ¡Por eso es raro!
Mi proyecto es de aprox. 2000 líneas de código y las usé gotosolo una vez porque era perezoso. Prometo cambiarlo por a breako a continuepero no me gusta boolean...
Su código parece inspeccionar un valor no inicializado de keep, si sus dos primeros condicionales (anidados) se evalúan como falsos. Eso es un error o no nos mostró todo el código relevante.
@IraBaxter Sí, se ve así pero está inicializado. Lo que más me preocupa es la corrección, la legibilidad y el mantenimiento, por lo que utilizo la herramienta Valgrind para resolver todos mis errores y pérdidas de memoria. Incluso si el programa se ejecuta "perfectamente", puede tener pérdidas de memoria que no se muestran hasta que un análisis descubre los errores.
¿A qué te refieres exactamente con "tabla de verdad"? ¿Quiere saber cuál es la condición completa bajo la cual se ejecuta cada bloque de código? (por ejemplo, para la parte entonces de "si (mantener)", la condición completa sería "*(ptr + j)[0] == '{' && !testFn(*(ptr + j)):? Debería actualice su pregunta para hacer explícito lo que quiere para esto.
Este código es muy extraño. Supongo que "ptr" se declara como "char *". ¿Cómo puede "ptr+j" ser cero? ¿Puedes explicar qué hace "*(ptr+j)[0]"? ¿Realmente compila?
@IraBaxter Por lo general, esto se hace con un "árbol de sintaxis abstracta" con un algoritmo de analizador de descenso recursivo, pero lo he hecho con bucles y una matriz que representa un programa donde la primera fila es la primera tubería y la primera columna de la primera fila es el primer argumento de la primera canalización. *(ptr + j)[0] en realidad, es muy concreto el carácter actual de lo que se está escaneando, ptres el comienzo de la tubería y j es el desplazamiento. 0 significa el primer carácter del argumento. Especificación [aquí](pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html).
Bien, entonces "ptr" se declara "**char". ¿Cómo puede "ptr+j" ser cero? No respondió a la pregunta sobre lo que quiere explícitamente para las "tablas de verdad". (¿Todos esos tamaños de matriz están realmente cableados? Este no es un código preprocesado; todavía contiene comentarios). (Sí, conozco los analizadores de descenso recursivo, pero generalmente no los implemento así: consulte stackoverflow.com/questions/2245962/… )
Podría hacerlo con recursividad (que se usa para llamar forky exec, pero los bucles son más rápidos y fáciles de codificar que la recursividad. Creo que la idea es buena para hacer una tabla de verdad para las condiciones para ver si algo siempre es o nunca es cierto porque hasta ahora no tengo experiencia en este nivel detallado de puntero C, pero estoy trabajando en ello. Puede encontrar el repositorio completo aquí para esta cosa, que es mi propio shell similar a sasho dash. Es interesante que Valgrind pueda Encontré mucho sobre la RAM, escribí una prueba que usa Valgrind.
No es una herramienta automatizada, sino una forma estándar de escribir programas sin usar variables adicionales: stackoverflow.com/a/36661381/120163
El análisis no puede modificar su código. El análisis es una operación de sólo lectura. Tal vez familiarizarse primero con algunos conceptos, por ejemplo, pluralsight.com/courses/brownfield Escribir pruebas unitarias, medir la cobertura del código o mejorar la cobertura de las sucursales. Luego busque un IDE que tenga métodos de refactorización como "Extraer método" e "Invertir si declaración", algo así como Jetbrains Resharper pero para C. Identifica errores lógicos como "Esta condición si siempre es verdadera".
Reescriba esto con clases (c++) y polimorfismo y evite las ramificaciones. stackoverflow.com/questions/519515/…
Podría considerar simplemente reorganizar el código para que sea legible. Consulte stackoverflow.com/q/37079307/120163
@IraBaxter Sí. Mi IDE me dice cuando una condición es "siempre verdadera" y luego puedo eliminarla. Hoy estoy aprendiendo cómo hacer una gramática pero estoy atascado en cómo implementar reglas. Intento esto con el analizador de limón para una whilepalabra clave expr(A)::= WHILE LPAR expr(B) RPAR expr(C). {printf("test"); }, pero la cadena de prueba no se imprime. Mi código se desordenó y estoy tratando de hacer una gramática en su lugar.
Realmente, realmente no quieres construir tu propio analizador C. Primero, es mucho más difícil de lo que parece, porque C es mucho más complicado de lo que crees, los compiladores no están de acuerdo sobre lo que es legal y tienes que hacer que el preprocesador sea correcto. Si logra hacer todo eso, su próximo problema es que un analizador no es lo suficientemente bueno para hacer mucho con él. Vea mi ensayo en semanticdesigns.com/Products/DMS/LifeAfterParsing.html
@IraBaxter ¡Gracias, Ira! Marqué los enlaces. Pero debo aprender a escribir gramática. Estoy aprendiendo el analizador de limón y casi puedo hacer la whilepalabra clave en función de la calculadora simple. Ahora dicen en la revisión del código hoy que mi código muestra "muchas" mejoras: codereview.stackexchange.com/questions/128149/…

Respuestas (1)

Tuve cierto éxito con una herramienta llamada CppCheck a través de un sistema Jenkins CI. No realizo un seguimiento específico de las ramas condicionales, pero la cantidad de comprobaciones que proporciona esta herramienta merece la pena. En particular, verifique la Conditionparte que enumera varias verificaciones de condiciones que siempre son verdaderas/falsas (incluido el seguimiento de valores, condiciones duplicadas, lógica de intervalo), pero otras categorías también enumeran algunas verificaciones posiblemente útiles como:

  • "adición de puntero en condición"
  • “código idéntico en ambas ramas de if/else u operador ternario.”
  • otro tipo de condiciones sospechosas o redundantes (para STL, cadenas, operaciones lógicas/numéricas, ...)

Y está disponible como complemento para su IDE.

“código idéntico en ambas ramas de if/else u operador ternario.” Guau. ¿Ese es el ejemplo más interesante de lo que puede hacer CppCheck? He estado codificando durante 45 años y nunca me había encontrado con esto.