我尝试编写一个程序,将一串字符转换为大写字母。父进程应该获取字符串并通过管道将其发送给子进程。然后子进程应该从管道中读取,把所有的字母都变成Big letters打印在控制台上。我真的不明白为什么我的管道不工作。当父母甚至没有在其中写东西时,孩子试图阅读管道可能是一个问题。
提前谢谢你。
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#define BUF 64
/*
Aufgabe:
Elternprozess: Gibt einen Array mit Klein- und Großbuchstaben mit einer pipe an dem Kindprozess weiter
Kindprozess: Verwanldelt alle Zeichen in Großbuchstaben
*/
int main(void) {
pid_t pid;
int fd[2];
pipe(fd);
char *buffer = malloc(sizeof(char)*BUF);
switch(pid = fork()) {
case -1:
perror("FEHLER BEI FORK\n");
break;
case 0: //Kindprozess
close(fd[1]);
char c;
int i = 0;
while(1) {
read(fd[0], &c, 1);
if(c == '\0') {
break;
} else if(c > 64 || c < 123) {
buffer[i] = c;
i++;
}
}
for(int i = 0; i < BUF; i++) {
if(buffer[i] > 96 && buffer[i] < 123) {
buffer[i] -= 32;
}
}
printf("Die Zeichen in Großbuchstaben sind:\n\n");
for(int i = 0; i < BUF; i++) {
if(buffer[i] != 0 && buffer[i] != 48) {
printf("%c", buffer[i]);
printf(" ");
}
}
free(buffer);
break;
default: //Elternprozess
close(fd[0]);
char string1[] = "a b c d \0";
memset(buffer, 0, BUF);
strcpy(buffer, string1);
int len = strlen(buffer) + 1;
int n;
int total = 0;
while(n < len) {
n = write(fd[1], buffer, strlen(buffer));
if(n < 0) {
perror("FEHLER BEIM SENDEN\n");
break;
}
total += n;
}
printf("Nachricht gesendet.\n");
free(buffer);
wait(NULL);
break;
}
}
问题是读取进程一直读到 NUL 字符,但它永远不会到来,缓冲区溢出,孩子死了。
NUL 字符永远不会被读取,因为它从未被写入。仔细看write loop,稍微简化一点就是:
int len = strlen(buffer) + 1;
int total = 0;
while(n < len) {
n = write(fd[1], buffer, strlen(buffer));
total += n;
}
请注意,您在
len
中计算要写入的字节数,包括 NUL,但随后您写入 strlen(buffer)
字节,without 计算 NUL。然后将写入的字节添加到 total
但未使用该变量。因此,您正在无休止地编写没有 NUL 的字符串副本。
解决方案是这样的:
let len = strlen(buffer) + 1;
int total = 0;
while(total < len) {
n = write(fd[1], buffer + total, len - total);
if (n < 0) { /*...*/ }
total += n;
}
即使进行了上述更正,您的子进程仍然会调用 Undefined Behavior 写入超出字符串末尾的内容。当你应该只迭代
BUF
次时,你迭代了 i
次。此外,您在孩子身上留下了i
的阴影:
int i = 0;
while(1) {
read(fd[0], &c, 1);
if(c == '\0') {
break;
} else if(c > 64 || c < 123) {
buffer[i] = c;
i++;
}
}
for(int i = 0; i < BUF; i++) {
...
}
printf("Die Zeichen in Großbuchstaben sind:\n\n");
for(int i = 0; i < BUF; i++) {
...
如果您将
-Wshadow
添加到 gcc
的编译字符串中,您总能发现这个问题。此外,您可以通过简单地确保 buffer
以空字符终止然后迭代 buffer[i]
直到到达空字符来纠正迭代问题。这简化了你的逻辑。
下面迭代
for(i = 0; buffer[i]; i++)
来避免这个问题。这是迭代 nul 终止字符串中字符的正确方法,而无需事先知道长度(这就是 nul 终止字符的用途)
(编辑:-组合循环,用 ctype.h 宏简化)
case 0: //Kindprozess
close(fd[1]);
char c;
int i = 0;
while(1) {
read(fd[0], &c, 1);
if(c == 0) {
break;
}
else if (isalpha ((unsigned char)c)) { /* is A-Za-z ? */
buffer[i] = toupper ((unsigned char)c); /* make uppwer case */
i++;
}
}
buffer[i] = 0; /* nul-terminate at i */
printf("Die Zeichen in Großbuchstaben sind:\n\n");
for(i = 0; buffer[i]; i++) {
printf("%*c", i ? 3 : 0, buffer[i]); /* write 3 spaces except 1st */
}
putchar ('\n');
free(buffer);
break;
尽可能避免使用MagicNumbers。不要使用
48
使用 '0'
(这对你如何使用它毫无意义)。不要在表示 >96
时使用 'a'
或在表示 <123
时使用 'z'
。改用文字字符——更具可读性。
此外,包括
ctype.h
并利用为 isalpha()
、isupper()
、islower()
等提供的宏。比 >96
和 <123
更具可读性。
事实上,如果您使用
ctype.h
宏,则不需要手动测试字符值。在您的代码中,如果它们是 alpha 字符,您只包含从父级发送的字符。为此,您可以简单地使用isalpha()
。
如果使用
toupper()
宏,则在转换为大写字母时无需测试——它只会将小写字母字符转换为大写字母,并在内部提供测试。
在输出行末尾留下尾随空格不是一个好主意。您可以通过在除第一个字符输出之外的所有字符之前写入 3 个空格来控制输出字符之间的 3 个空格。 printf()
中的一个简单的 三元 和
field-width使这变得简单,例如
printf("%*c", i ? 3 : 0, buffer[i]);
试一试其他答案中的更正,您的程序应该按预期执行。
示例使用/输出
$ ./bin/pipe-fork-uppercase
Nachricht gesendet.
Die Zeichen in Großbuchstaben sind:
A B C D
额外的想法
您的代码中还有其他几点有点尴尬。
free()
完成后的内存。sizeof(char)
被定义为 1
所以它可以在调用 malloc()
如果你分配(例如 malloc(BUF)
是所有需要的),buffer
。您可以简单地将string1
的内容写入管道。 buffer
只能在子进程中声明为自动存储持续时间。很好地定义了常数BUF
,'\n'
以使程序符合POSIX。如果不这样做,就会在任何未命名为 windows 的操作系统中弄乱下一个终端提示。 putchar('\n');
是所有需要的。把它放在一起,让字符串通过
char string1[] = "a b c d*E_F+G-h ";
更有趣一点,你会:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
#define BUF 64
/*
Aufgabe:
Elternprozess: Gibt einen Array mit Klein- und Großbuchstaben mit einer pipe an dem Kindprozess weiter
Kindprozess: Verwanldelt alle Zeichen in Großbuchstaben
*/
int main (void) {
pid_t pid;
int fd[2];
pipe (fd);
switch (pid = fork()) {
case -1:
perror ("FEHLER BEI FORK\n");
break;
case 0: //Kindprozess
close (fd[1]);
char c, buffer[BUF];
int i = 0;
while (1) {
read (fd[0], &c, 1);
if (c == 0) {
break;
}
else if (isalpha ((unsigned char)c)) { /* is A-Za-z ? */
buffer[i] = toupper ((unsigned char)c); /* make uppwer case */
i++;
}
}
buffer[i] = 0; /* nul-terminate at i */
printf ("Die Zeichen in Großbuchstaben sind:\n\n");
for (i = 0; buffer[i]; i++) {
printf ("%*c", i ? 3 : 0, buffer[i]); /* write 3 spaces except 1st */
}
putchar ('\n'); /* tidy up with newline */
break;
default: //Elternprozess
close (fd[0]);
char string1[] = "a b c d*E_F+G-h ";
int len = strlen (string1) + 1,
n,
total = 0;
while (total < len) {
n = write (fd[1], string1 + total, len - total);
if (n < 0) {
perror ("FEHLER BEIM SENDEN\n");
break;
}
total += n;
}
printf ("Nachricht gesendet.\n");
wait (NULL);
break;
}
}
示例使用/输出
$ ./bin/pipe-fork-uppercase
Nachricht gesendet.
Die Zeichen in Großbuchstaben sind:
A B C D E F G H