【问题标题】:How do strcat() and read() work with '\0' in Cstrcat() 和 read() 如何在 C 中使用 '\0'
【发布时间】:2019-01-05 15:37:05
【问题描述】:

这是我的整个代码:

 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() 是如何工作的呢? 我不应该在调用它们之前插入任何空终止符吗? 垃圾值呢?

【问题讨论】:

  • read(p[0], _1st_child_out, sizeof("1st child output")) 正在读入一个无效的未初始化指针。
  • 它从 p[0] 中提供的文件描述符读取输入(在 pipe() 调用中写入),并存储已读取的内容(即在 write( ) 到 _1st_child_out *char
  • 我认为pipe()fork() 调用不会改变任何东西——你能用更小的程序重现错误吗?
  • @AhmedRehan: _1st_child_out 是一个未初始化的指针。您无法将任何内容读入它所指向的内容。
  • @Blacksilver 作为当前的代码,没有任何错误,如果我将未初始化的字符指针“_1st_child_out”设为 NULL,则不会在其中写入或连接任何内容,我不是完全掌握管道、写入和读取的效果我不确定是哪个导致错误,无论如何我想知道两者如何处理这种情况

标签: c null pipe strcat


【解决方案1】:

您的代码中存在一些错误,这是我的观察。

案例 1:- 在您的代码中,您调用了两次 fork(),管道写入端 p[1] 在第一个 fork() rc1 进程中包含一些数据 1st child output,但是您的代码试图在第二个rc2 进程中读取表单p[0]

您应该检查read() 返回值是否成功,或者是否从错误/未初始化的文件描述符中读取。这个

    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 */
   }

由于数据在rc1 进程中写入p[1],而不是在rc2 进程中,但是当您尝试从p[0] 读取数据时,它会给您

读取:错误地址

案例 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,这是有效的但不适用于\0,因为它只是一个字符。

但是当你用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 的手册页中找到

strcat() 函数将src 字符串附加到dest 字符串, 覆盖终止空字节 ('\0') dest 的结尾,然后添加一个终止空字节。字符串不能重叠,dest 字符串必须 结果有足够的空间。如果dest 不够大,程序行为不可预测缓冲区溢出 运行是攻击安全程序的常用途径

   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.

【讨论】:

  • 在同一父级的两个子级之间创建管道的最佳方法是什么? (这在书籍作业中特别问过)为什么 read() 可能从错误/未初始化的文件描述符中读取?最初的几个谷歌结果从未深入探讨这些功能,是否有更彻底的特定文档?
【解决方案2】:

READ(2) 不关心'\0',它不关心他读取的任何值。 STRCAT(3) 将从给定 const char *src 指针末尾的“\0”读取和写入(操作,而不是函数)。

据我所见,_1st_child_out 是一个未初始化的指针。 在 READ(2) 中的某个地方会有一些东西

dest[i] = array_bytes[i];

但是在这里,您的char *_1st_child_out 被赋予了一个完全“随机”的值,您只会在内存中随机写入某个位置,大多数情况下它会导致分段错误,并且您的程序会崩溃。

那么你需要注意你在这里使用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",您的程序很可能会崩溃。

【讨论】:

    【解决方案3】:

    您的第 19 行有一个未指向任何已分配内存的指针。将其更改为 char 数组将解决问题。

    char _1st_child_out[100];

    另外,为了安全起见,请勿使用strcatstrcpy 等所有 str 函数都不会检查目标边界。他们都有一个n 版本。 strcat 应该替换为strncat,这将使用第三个参数来指定 cat 的最大长度,因此不会发生缓冲区溢出。

    【讨论】:

      猜你喜欢
      • 2010-09-29
      • 1970-01-01
      • 1970-01-01
      • 2017-03-15
      • 1970-01-01
      • 2016-01-21
      • 1970-01-01
      • 1970-01-01
      • 2016-05-20
      相关资源
      最近更新 更多