编写忽略操作顺序的 C 程序?

Writing a C program that ignores the order of operations?

我想编写一个 C 程序,从用户那里获取一个简单的数学问题,并使用不同的进程来解决每个部分,然后将值发送回父进程。这个程序不要讲究操作顺序。例如,如果我有问题:

3 + 4 / 7 + 4 * 2

我希望我的程序输出以下内容:

Enter problem: 3 + 4 / 7 + 4 * 2
PID 20419 calculated 3+4 as 7
PID 20420 calculated 7/7 as 1
PID 20421 calculated 1+4 as 5
PID 20422 calculated 5*2 as 10
Final result: 10

我在解析输入的方程式时遇到了一些问题。我正在考虑使用 C 中的 getline() 方法来帮助我解析输入。这是我目前所拥有的:

#include <stdio.h>

#define MAX_LINE_LENGTH 200

int main()
{
    printf("Enter equation: ");
    //getline(&buffer, &size, stdin)
    //&buffer is the address of the first character
    //&size is the address of the variable that holds the size of the input buffer
    //stdin is the input file handle
    size_t n = MAX_LINE_LENGTH;

    //Line becomes a pointer to 200 bytes of memory for you to use
    char *line = malloc(n)
    while ((getline(&line, &n, stdin)) {
        //Parse numbers and operators here
    }

    return 0;
}

有人对如何解析我将从标准输入输入的数字和运算符有任何建议吗?一旦我认为我读入了数字、运算符和数字,我想使用 fork()。提前谢谢大家。

这并没有完全回答问题,但它是一个有趣的小玩具。这不处理最终数据点,无疑需要加强("robustification" 是一个词吗?应该是!)。作为 reader 的练习,重构代码以处理最终输出,并处理非整数值。

#include <stdio.h>
#include <ctype.h>

int main(void) {
        int c;
        int last_op = 0;
        float a[2] = {0};

        while( ( c = getchar()) != EOF ) {
                if(isspace(c)) {
                        continue;
                }
                if(isdigit(c)) {
                        a[1] = 10 * a[1] + c - '0';
                } else {
                        float tmp = a[1];
                        if( last_op ) {
                                switch(last_op) {
                                case '+': tmp = a[0] + a[1]; break;
                                case '/': tmp = a[0] / a[1]; break;
                                case '*': tmp = a[0] * a[1]; break;
                                case '-': tmp = a[0] - a[1]; break;
                                default: fprintf( stderr, "invalid input: %c", last_op );
                                }
                                printf ("calculated %f %c %f = %f\n", a[0], last_op, a[1], tmp);
                        }
                        a[0] = tmp;
                        a[1] = 0;
                        last_op = c;
                }
        }
}

一点点 "robustified" 确实获取最终数据点的加法可能类似于以下内容(有意使用整数数学,更改为浮动-如果需要点):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>

#define MAX_LINE_LENGTH 200

int main()
{
    size_t  n = MAX_LINE_LENGTH,
            offset = 0;
    int oper = 0, v1 = INT_MIN;
    char *line = malloc(n);

    if (!line) {    /* validate every allocation */
        perror ("malloc-line");
        return 1;
    }

    printf("Enter equation: ");
    if (getline (&line, &n, stdin) == -1) { /* validate every input */
        fputs ("stream error or user canceled.\n", stderr);
        return 1;
    }

    for (; line[offset];) {     /* loop while not end of line */
        int nchar = 0, v2;
        if (sscanf (line + offset, "%d%n", &v2, &nchar) == 1) { /* read int? */
            if (v1 == INT_MIN)  /* if v1 not set, set v1 to value */
                v1 = v2;
            else {              /* otherwise its v2 */
                int vsave = v1;
                switch (oper) { /* switch on operator, save result in v1 */
                    case '+':   v1 += v2; break;
                    case '-':   v1 -= v2; break;
                    case '*':   v1 *= v2; break;
                    case '/':   v1 /= v2; break;
                    default:    fputs ("error: invalid operator.\n", stderr);
                                break;
                }
                /* output results of calculation */
                printf ("calculated %d%c%d as %d\n", vsave, oper, v2, v1);
            }
            offset += nchar;    /* update offset with nchars read */
        }
        else {  /* read of int failed, must be oper or end */
            size_t used = strcspn (&line[offset], "+-*/");  /* chars to oper */
            offset += used;     /* update offset */
            if (line[offset] == '\n' || line[offset] == 0)  /* end of line? */
                break;
            oper = line[offset++];  /* set oper advance to next char */
        }
    }

    free (line);    /* don't forget to free line */
}

(注意:你分配了malloc,甚至没有直接分配,如果line = NULL;n = 0; getline 会自己分配足够的 space,所以不要忘记 free(line); 释放你分配的内存。(是的,这里发生在退出时,但希望你会编写使用的程序getline 多于 main(),所以现在要养成好习惯...)

上面 "%n" 说明符的使用将 sscanf 在读取中消耗的字符数放入 nchars 变量中,然后用于更新 offsetline.

例子Use/Output

$ ./bin/calculate
Enter equation: 3 + 4 / 7 + 4 * 2
calculated 3+4 as 7
calculated 7/7 as 1
calculated 1+4 as 5
calculated 5*2 as 10

fork() 处理计算

本质上,在您拥有 v1, v2oper 之后,唯一需要的更改是 fork() 流程,然后处理子进程中的 switch() 语句,然后在父级 wait() 中,直到子级退出,然后再退出并获取下一组值。如我的评论所述,man 2 wait 中的示例很好地概述了该过程。

根据您的评论,您在 sscanf 调用成功后将 fork() 设置在了正确的位置。您所做的就是添加对 fork() 的调用,并验证和确定哪个是子项和父项。然后将现有计算移动到子进程中,并添加对 getpid() 的调用以添加到您的输出中。在父进程中,启动您的 wait(),然后在您的子进程完成后退出。

您可以在进行上述更改后实施以下操作:

        if (sscanf (line + offset, "%d%n", &v2, &nchar) == 1) { /* read int? */
            if (v1 == INT_MIN)  /* if v1 not set, set v1 to value */
                v1 = v2;
            else {      /* otherwise its v2 */
                int status;
                pid_t w, pid = fork();  /* fork to compute values */
                if (pid == -1) {        /* validate fork succeeded */
                    perror ("fork");
                    return 1;
                }
                if (pid == 0) {         /* if pid == 0 in child process */
                    int vsave = v1;
                    switch (oper) { /* switch on operator, save result in v1 */
                        case '+':   v1 += v2; break;
                        case '-':   v1 -= v2; break;
                        case '*':   v1 *= v2; break;
                        case '/':   v1 /= v2; break;
                        default:    fputs ("error: invalid oper.\n", stderr);
                                    break;
                    }   /* output child PID with results of calculation */
                    printf ("PID %ld calculated %d%c%d as %d\n", 
                            (long)getpid(), vsave, oper, v2, v1);
                }
                else {      /* in the parent process */
                    do {    /* wait on child PID */
                        w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
                        if (w == -1) {      /* validate waitpid return */
                            perror("waitpid");
                            exit (EXIT_FAILURE);
                        }
                    } while (!WIFEXITED(status) && !WIFSIGNALED(status));
                    exit (EXIT_SUCCESS);    /* child exited, exit parent */
                }
            }
            offset += nchar;    /* update offset with nchars read */
        }

完整的示例只是添加了所需的头文件,而其余代码保持不变。例如:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <sys/wait.h>
#include <unistd.h>

int main (void)
{
    size_t  n = 0,
            offset = 0;
    int oper = 0, v1 = INT_MIN;
    char *line = NULL;

    printf("Enter equation: ");
    if (getline (&line, &n, stdin) == -1) { /* validate every input */
        fputs ("stream error or user canceled.\n", stderr);
        return 1;
    }

    for (; line[offset];) {     /* loop while not end of line */
        int nchar = 0, v2;
        if (sscanf (line + offset, "%d%n", &v2, &nchar) == 1) { /* read int? */
            if (v1 == INT_MIN)  /* if v1 not set, set v1 to value */
                v1 = v2;
            else {      /* otherwise its v2 */
                int status;
                pid_t w, pid = fork();  /* fork to compute values */
                if (pid == -1) {        /* validate fork succeeded */
                    perror ("fork");
                    return 1;
                }
                if (pid == 0) {         /* if pid == 0 in child process */
                    int vsave = v1;
                    switch (oper) { /* switch on operator, save result in v1 */
                        case '+':   v1 += v2; break;
                        case '-':   v1 -= v2; break;
                        case '*':   v1 *= v2; break;
                        case '/':   v1 /= v2; break;
                        default:    fputs ("error: invalid oper.\n", stderr);
                                    break;
                    }   /* output child PID with results of calculation */
                    printf ("PID %ld calculated %d%c%d as %d\n", 
                            (long)getpid(), vsave, oper, v2, v1);
                }
                else {      /* in the parent process */
                    do {    /* wait on child PID */
                        w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
                        if (w == -1) {      /* validate waitpid return */
                            perror("waitpid");
                            exit (EXIT_FAILURE);
                        }
                    } while (!WIFEXITED(status) && !WIFSIGNALED(status));
                    exit (EXIT_SUCCESS);    /* child exited, exit parent */
                }
            }
            offset += nchar;    /* update offset with nchars read */
        }
        else {  /* read of int failed, must be oper or end */
            size_t used = strcspn (&line[offset], "+-*/");  /* chars to oper */
            offset += used;     /* update offset */
            if (line[offset] == '\n' || line[offset] == 0)  /* end of line? */
                break;
            oper = line[offset++];  /* set oper advance to next char */
        }
    }

    free (line);    /* don't forget to free line */
}

例子Use/Output

运行 以上具有相同输入的内容将在每次单独计算之前产生与子 PID 相同的输出,例如

$ ./bin/calculate_fork
Enter equation: 3 + 4 / 7 + 4 * 2
PID 18746 calculated 3+4 as 7
PID 18747 calculated 7/7 as 1
PID 18748 calculated 1+4 as 5
PID 18749 calculated 5*2 as 10

试一试,如果您还有其他问题,请告诉我。