我试着写一个程序,把一串字符变成大写字母。父进程应该接受字符串,并通过管道将其发送给子进程。然后,子进程应该从管道中读取,将所有字母转换为大写字母,并将其打印到控制台上。我真不明白为什么我的管子坏了。这可能是一个问题,当父进程甚至没有在管道中写入任何内容时,子进程试图读取管道。
提前谢谢你。
#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 FORKn");
break;
case 0: //Kindprozess
close(fd[1]);
char c;
int i = 0;
while(1) {
read(fd[0], &c, 1);
if(c == ' ') {
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:nn");
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 ";
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 SENDENn");
break;
}
total += n;
}
printf("Nachricht gesendet.n");
free(buffer);
wait(NULL);
break;
}
}
问题是读进程一直读到NUL字符,但这永远不会出现,缓冲区溢出,子进程死亡。
NUL字符永远不会被读取,因为它永远不会被写入。仔细看一下写循环,稍微简化一下是:
int len = strlen(buffer) + 1;
int total = 0;
while(n < len) {
n = write(fd[1], buffer, strlen(buffer));
total += n;
}
请注意,您在len
中计算要写入的字节数,包括NUL,但随后您写入strlen(buffer)
字节,不包含计数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;
}
即使有了上面的更正,您的子进程仍然调用未定义行为写入超出字符串末尾的内容。当你应该只迭代i
次时,你迭代了BUF
次。此外,您正在使用:
i
int i = 0;
while(1) {
read(fd[0], &c, 1);
if(c == ' ') {
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:nn");
for(int i = 0; i < BUF; i++) {
...
如果您将-Wshadow
添加到gcc
的编译字符串中,您总是可以捕获此问题。此外,只要确保buffer
以空字符结尾,然后在buffer[i]
上迭代,直到到达空字符,就可以纠正迭代问题。这简化了你的逻辑。
下面的迭代for(i = 0; buffer[i]; i++)
来避免这个问题。这是遍历以空结束的字符串中的字符的正确方法,而不必事先知道长度(这就是空结束字符的作用)
(编辑:-组合循环,用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:nn");
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'
(这对您使用它的方式没有意义)。当你说'a'
时不要说>96
,当你说'z'
时不要说<123
。使用文字字符代替——可读性更强。
另外,包括ctype.h
并利用为isalpha()
、isupper()
、islower()
等提供的宏。比>96
和<123
更具可读性。
实际上,如果使用ctype.h
宏,则不需要对字符值进行任何手动测试。在您的代码中,您只包含从父节点发送的alpha字符。您可以简单地使用isalpha()
来实现此目的。
如果您使用toupper()
宏,则不需要测试何时转换为大写字母——它只会将小写字母转换为大写字母,并在内部提供测试。
在输出行末尾留下尾随空格不是一个好主意。您可以通过在除第一个字符输出之外的所有字符前写3个空格来控制输出字符之间的3个空格。printf()
中一个简单的三元制和字段宽度使其变得简单,例如
printf("%*c", i ? 3 : 0, buffer[i]);
把这个和另一个答案的更正一起试一试,你的程序应该像你期望的那样运行。
使用/输出示例
$ ./bin/pipe-fork-uppercase
Nachricht gesendet.
Die Zeichen in Großbuchstaben sind:
A B C D
额外的想法
你的代码中还有其他一些地方有点尴尬。
- 虽然动态分配没有什么问题,但是对于64字节,使用堆栈上的自动存储的简单数组声明如果可以,并且在完成时不需要跟踪和
free()
内存。 sizeof(char)
被定义为1
,所以如果你分配(例如malloc(BUF)
是所有需要的),它可以在调用malloc()
时被省略,buffer
在父进程中不需要。您可以简单地将string1
的内容写入管道。buffer
只能在子进程中声明自动存储持续时间。很好地定义了常量BUF
,- 当从程序中写入输出时,不要忘记在最终输出后输出
'n'
以使程序符合POSIX。如果不这样做,在任何非命名窗口的操作系统中都会出现下一个终端提示符。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 FORKn");
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:nn");
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 SENDENn");
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