strcat() 和 read() 如何与 C 中的 '\0' 一起使用
How do strcat() and read() work with '\0' in C
首先是我的全部代码:
1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <sys/wait.h>
5. #include <string.h>
6. int main(int argc, char *argv[]) {
7. int p[2]; // p[0]: file descriptor for read end of pipe
8. // p[1]: file descriptor for write end of pipe
9. if (pipe(p) < 0) exit(1);
10. int rc1 = fork();
11. if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
12. else if (rc1 == 0){ write(p[1], "1st child output",
13. sizeof("1st child output")); }
14. else{
15. int rc2 = fork();
16. if (rc2 < 0){ fprintf(stderr, "fork error\n"); }
17. else if (rc2 == 0){
18. printf("2st child output\n");
19. char *_1st_child_out;
20. read(p[0], _1st_child_out, sizeof("1st child output"));
21. strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
22. printf("%s\n", _1st_child_out);
23. }
24. }
25. }
如果我初始化 19:13:
char *_1st_child_out;
带有 '\0' 或 NULL,
字符串保持为空并且 22:13:
printf("%s\n", _1st_child_out);
什么都不打印,那么 strcat() 和 read() 是如何工作的呢?
我不应该在调用它们之前插入任何空终止符吗?
垃圾值呢?
您的第 19 行有一个未指向任何已分配内存的指针。将其更改为字符数组即可解决问题。
char _1st_child_out[100];
此外,为了安全起见,请不要使用strcat
。 strcpy
等所有 str 函数都不检查目标边界。他们都有一个 n
版本。 strcat
应替换为 strncat
,它将采用第三个参数来指定 cat 的最大长度,因此不会发生缓冲区溢出。
READ(2)
不关心'\0',不关心他读到的any值。
STRCAT(3)
将从给定 const char *src
指针末尾的 '\0' 读取和写入(操作,而不是函数)。
据我所知,_1st_child_out
是一个未初始化的指针。
在 READ(2) 的某个地方会有一些东西
dest[i] = array_bytes[i];
但是在这里,你的 char *_1st_child_out
被赋予了一个总的 "random" 值,你将只是在你的内存中随机写入某个地方,大多数时候它会导致分段错误,并且你的程序会崩溃。
那么你需要注意你在这里使用 sizeof
运算符,
你给他 "1st child output"
,这将在 C 中被解释为 const char[]
并将 sizeof("1st child output")
替换为值 17(16 个字符和 '\0')。
你需要在其中指定一个类型,或者一个变量。
对于数组,如type array[x]
,sizeof(array)
将与sizeof(type) * x
相同。
为了修复你的程序,尝试创建一个静态分配的缓冲区,比如
char _1st_child_out[x];
其中 'x' 是缓冲区的大小(以字节为单位)。
或者尝试使用 MALLOC(3)
。
然后修复 READ(2)
.
的第三个参数
当您使用 STRCAT(3)
时,您需要知道如果给定目标缓冲区的大小不足以容纳 ", AFTER PIPE YA FOOL"
您的程序很可能会崩溃。
你的代码中有一些错误,这是我的观察。
案例 1:- 在您的代码中,您调用了 fork()
两次,管道写入端 p[1]
包含一些数据 1st child output
in第一个 fork()
rc1
进程,但您的代码试图在第二个 rc2
进程中读取表单 p[0]
。
您应该检查 read()
return 值是否成功,或者它是否正在从 wrong/uninitialized 文件描述符中读取。这个
char *_1st_child_out = NULL;
/* for process rc2, p[0] contains nothing, so what read() will read from p[0] ?? */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
由于数据写入 p[1]
是在 rc1
进程中,而不是在 rc2
进程中,但是当您尝试从 p[0]
读取数据时,它会给您
read: Bad address
情况 2 :- 克服上述问题的一种方法是
int main(int argc, char *argv[]) {
int p[2]; // p[0]: file descriptor for read end of pipe
// p[1]: file descriptor for write end of pipe
if (pipe(p) < 0) exit(1);
int rc1 = fork();
if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
else if (rc1 == 0){
write(p[1], "1st child output",sizeof("1st child output"));
}
else{
char *_1st_child_out = NULL;
/* read() will read from p[0] and store into _1st_child_out but _1st_child_out not holding any valid memory ? So it causes Undefined behavior */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
printf("%s\n", _1st_child_out);
}
return 0;
}
这里_1st_child_out
是一个指针,指针应该有有效的内存位置。您可以使用 NULL
进行初始化,即 (void*)0
,这是有效的,但使用 [=30=]
则无效,因为它只是一个字符。
但是当你用 NULL
初始化 _1st_child_out
并从 p[0]
读取数据并存储到 _1st_child_out
时,它会存储什么?它会导致分段错误,它也是 未定义的行为。
所以最好为 _1st_child_out
动态分配内存,然后调用 read()
或者像
这样创建堆栈分配数组
char _1st_child_out[10];
这是示例工作代码
int main(int argc, char *argv[]) {
int p[2]; // p[0]: file descriptor for read end of pipe
// p[1]: file descriptor for write end of pipe
if (pipe(p) < 0) exit(1);
int rc1 = fork();
if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
else if (rc1 == 0){
write(p[1], "1st child output",sizeof("1st child output"));
}
else{
char *_1st_child_out = malloc(BYTE); /* define BYTE value as how much memory needed, and free the dynamically allocated memory once job is done */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
/* make sure _1st_child_out has enough memory space to concatenate */
strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
printf("%s\n", _1st_child_out);
}
return 0;
}
注意:使用 strncat()
而不是 strcat()
,原因可以从 strcat()
https://linux.die.net/man/3/strcat 的手册页中找到
The strcat()
function appends the src
string to the dest
string,
overwriting the terminating null byte ('[=43=]'
) at
the end of dest
, and then adds a terminating null byte. The strings may not overlap, and the dest
string must
have enough space for the result. If dest
is not large enough, program behavior is unpredictable; buffer over‐
runs are a favorite avenue for attacking secure programs.
The strncat() function is similar, except that
* it will use at most n bytes from src; and
* src does not need to be null-terminated if it contains n or more bytes.
As with `strcat()`, the resulting string in dest is always null-terminated.
首先是我的全部代码:
1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <unistd.h>
4. #include <sys/wait.h>
5. #include <string.h>
6. int main(int argc, char *argv[]) {
7. int p[2]; // p[0]: file descriptor for read end of pipe
8. // p[1]: file descriptor for write end of pipe
9. if (pipe(p) < 0) exit(1);
10. int rc1 = fork();
11. if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
12. else if (rc1 == 0){ write(p[1], "1st child output",
13. sizeof("1st child output")); }
14. else{
15. int rc2 = fork();
16. if (rc2 < 0){ fprintf(stderr, "fork error\n"); }
17. else if (rc2 == 0){
18. printf("2st child output\n");
19. char *_1st_child_out;
20. read(p[0], _1st_child_out, sizeof("1st child output"));
21. strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
22. printf("%s\n", _1st_child_out);
23. }
24. }
25. }
如果我初始化 19:13:
char *_1st_child_out;
带有 '\0' 或 NULL, 字符串保持为空并且 22:13:
printf("%s\n", _1st_child_out);
什么都不打印,那么 strcat() 和 read() 是如何工作的呢? 我不应该在调用它们之前插入任何空终止符吗? 垃圾值呢?
您的第 19 行有一个未指向任何已分配内存的指针。将其更改为字符数组即可解决问题。
char _1st_child_out[100];
此外,为了安全起见,请不要使用strcat
。 strcpy
等所有 str 函数都不检查目标边界。他们都有一个 n
版本。 strcat
应替换为 strncat
,它将采用第三个参数来指定 cat 的最大长度,因此不会发生缓冲区溢出。
READ(2)
不关心'\0',不关心他读到的any值。
STRCAT(3)
将从给定 const char *src
指针末尾的 '\0' 读取和写入(操作,而不是函数)。
据我所知,_1st_child_out
是一个未初始化的指针。
在 READ(2) 的某个地方会有一些东西
dest[i] = array_bytes[i];
但是在这里,你的 char *_1st_child_out
被赋予了一个总的 "random" 值,你将只是在你的内存中随机写入某个地方,大多数时候它会导致分段错误,并且你的程序会崩溃。
那么你需要注意你在这里使用 sizeof
运算符,
你给他 "1st child output"
,这将在 C 中被解释为 const char[]
并将 sizeof("1st child output")
替换为值 17(16 个字符和 '\0')。
你需要在其中指定一个类型,或者一个变量。
对于数组,如type array[x]
,sizeof(array)
将与sizeof(type) * x
相同。
为了修复你的程序,尝试创建一个静态分配的缓冲区,比如
char _1st_child_out[x];
其中 'x' 是缓冲区的大小(以字节为单位)。
或者尝试使用 MALLOC(3)
。
然后修复 READ(2)
.
当您使用 STRCAT(3)
时,您需要知道如果给定目标缓冲区的大小不足以容纳 ", AFTER PIPE YA FOOL"
您的程序很可能会崩溃。
你的代码中有一些错误,这是我的观察。
案例 1:- 在您的代码中,您调用了 fork()
两次,管道写入端 p[1]
包含一些数据 1st child output
in第一个 fork()
rc1
进程,但您的代码试图在第二个 rc2
进程中读取表单 p[0]
。
您应该检查 read()
return 值是否成功,或者它是否正在从 wrong/uninitialized 文件描述符中读取。这个
char *_1st_child_out = NULL;
/* for process rc2, p[0] contains nothing, so what read() will read from p[0] ?? */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
由于数据写入 p[1]
是在 rc1
进程中,而不是在 rc2
进程中,但是当您尝试从 p[0]
读取数据时,它会给您
read: Bad address
情况 2 :- 克服上述问题的一种方法是
int main(int argc, char *argv[]) {
int p[2]; // p[0]: file descriptor for read end of pipe
// p[1]: file descriptor for write end of pipe
if (pipe(p) < 0) exit(1);
int rc1 = fork();
if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
else if (rc1 == 0){
write(p[1], "1st child output",sizeof("1st child output"));
}
else{
char *_1st_child_out = NULL;
/* read() will read from p[0] and store into _1st_child_out but _1st_child_out not holding any valid memory ? So it causes Undefined behavior */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
printf("%s\n", _1st_child_out);
}
return 0;
}
这里_1st_child_out
是一个指针,指针应该有有效的内存位置。您可以使用 NULL
进行初始化,即 (void*)0
,这是有效的,但使用 [=30=]
则无效,因为它只是一个字符。
但是当你用 NULL
初始化 _1st_child_out
并从 p[0]
读取数据并存储到 _1st_child_out
时,它会存储什么?它会导致分段错误,它也是 未定义的行为。
所以最好为 _1st_child_out
动态分配内存,然后调用 read()
或者像
char _1st_child_out[10];
这是示例工作代码
int main(int argc, char *argv[]) {
int p[2]; // p[0]: file descriptor for read end of pipe
// p[1]: file descriptor for write end of pipe
if (pipe(p) < 0) exit(1);
int rc1 = fork();
if (rc1 < 0){ fprintf(stderr, "fork error\n"); }
else if (rc1 == 0){
write(p[1], "1st child output",sizeof("1st child output"));
}
else{
char *_1st_child_out = malloc(BYTE); /* define BYTE value as how much memory needed, and free the dynamically allocated memory once job is done */
int ret = read(p[0], _1st_child_out, sizeof("1st child output"));
if(ret == -1) {
perror("read");
/* error handling */
}
/* make sure _1st_child_out has enough memory space to concatenate */
strcat(_1st_child_out, ", AFTER PIPE YA FOOL");
printf("%s\n", _1st_child_out);
}
return 0;
}
注意:使用 strncat()
而不是 strcat()
,原因可以从 strcat()
https://linux.die.net/man/3/strcat 的手册页中找到
The
strcat()
function appends thesrc
string to thedest
string, overwriting the terminating null byte ('[=43=]'
) at the end ofdest
, and then adds a terminating null byte. The strings may not overlap, and thedest
string must have enough space for the result. Ifdest
is not large enough, program behavior is unpredictable; buffer over‐ runs are a favorite avenue for attacking secure programs.The strncat() function is similar, except that * it will use at most n bytes from src; and * src does not need to be null-terminated if it contains n or more bytes. As with `strcat()`, the resulting string in dest is always null-terminated.