Как strcat() и read() работают с '\0' в 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()? я не должен вставлять нулевые терминаторы перед вызовом их? как насчет значений мусора?

3 ответа

Решение

В вашем коде есть несколько ошибок, вот мои наблюдения.

Случай 1:- В вашем коде вы звоните fork() два раза, конец записи трубы p[1] содержит некоторые данные 1st child output во-первых fork()rc1 процесс, но ваш код пытался прочитать форму p[0] в секунду rc2 процесс.

ты должен проверить 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 */
   }

Поскольку данные записаны в p[1] в rc1 процесс, а не в 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 так как это всего лишь один символ.

Но когда вы инициализируете _1st_child_out с NULL и читать данные из 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) не заботится о '\0', его не волнует любое значение, которое он читает.STRCAT(3) будет читать и писать (операции, а не функции) из '\ 0' в конце данного const char *src указатель.

Из того, что я вижу, _1st_child_out неинициализированный указатель Где-то в READ(2) будет что-то

dest[i] = array_bytes[i];

Но здесь ваш char *_1st_child_out дается общее "случайное" значение, и вы просто напишите в вашу память что-то случайное, в большинстве случаев это приведет к ошибке сегментации, и ваша программа потерпит крах.

Тогда вам нужно позаботиться о вашем использовании sizeof оператор здесь, вы даете ему "1st child output" который будет интерпретироваться как 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" Ваша программа имеет огромный шанс сбоя.

Ваша строка 19 имеет указатель, который не указывает ни на какую выделенную память. Измените его на массив символов, чтобы решить проблему.

char _1st_child_out[100];

Кроме того, для безопасности, не используйте strcat, Все функции str, такие как strcpy etc не проверяет границу назначения. У них всех есть n версия. strcat следует заменить на strncat, который примет третий параметр, чтобы указать максимальную длину cat, чтобы переполнение буфера не происходило.

Другие вопросы по тегам