Как 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, чтобы переполнение буфера не происходило.