问题
I want to write a C program that takes a simple math problem from the user and use different processes to solve each part, and then send the value back to the parent process. This program should not pay attention to the order of operations. For instance, if I have the problem:
3 + 4 / 7 + 4 * 2
I want my program to output the following:
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
I am having a bit of trouble parsing the equation I am entering. I am thinking of using the getline()
method in C to help me parse input. This is what I have so far:
#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;
}
Does anyone have any suggestions on how to parse the numbers and operators that I will enter from the standard input? Once I think I read in a number, operator, and a number, I would like to use fork()
. Thank you all in advance.
回答1:
A slight bit of a "robustified" addition that does grab the final data point could be something like the following (use of integer math intentional, change to floating-point if desired):
#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 */
}
(note: you have allocated with malloc
, and even absent the direct allocation, if line = NULL;
and n = 0;
getline
will allocate sufficient space on its own, so don't forget to free(line);
to free the memory you have allocated. (yes, here that happens on exit, but hopefully you will write programs that use getline
in more than main()
, so build good habits now...)
The use of the "%n"
specifier above places the number of characters consumed by sscanf
in that read in the nchars
variable that is then used to update your offset
in line
.
Example 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()
To Handle Calculations
Essentially, the only changes needed after you have your v1, v2
and oper
is to fork()
the process and then handle the switch()
statement in the child and then in the parent wait()
until the child exits before exiting and grabbing your next set of values. As indicated in my comment, the example in man 2 wait
provides a good outline of the process.
Based on your comment, you were setting fork()
up in the correct location after the sscanf
call succeeds. There all you are doing is adding the call to fork()
and the validations and determination of which is the child and parent. Then move the existing calculation into the child process, and add a call to getpid()
to add to your output. In the parent process, site up your wait()
and then exit after your child is done.
You can implement the following making the changes above:
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 */
}
The full example simply adds the needed header files with the remainder of the code unchanged. For example:
#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 */
}
Example Use/Output
Running the above with the same input will produce the same output with the child PID before each individual calculation, e.g.
$ ./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
Give it a go and let me know if you have further questions.
回答2:
This doesn't completely answer the question, but was a fun little toy. This does not handle the final data point, and undoubtedly needs to be robustified (is "robustification" a word? It ought to be!). As an exercise for the reader, refactor the code to handle the final output, and deal with non-integer values.
#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;
}
}
}
来源:https://stackoverflow.com/questions/58220989/writing-a-c-program-that-ignores-the-order-of-operations