Вопрос
Второй звонок к strcat
Здесь генерирует ошибку сегментации, почему?
#include <unistd.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
int main (int argc, char * argv[])
{
char command[250];
//if(scanf("%199s", command) == 1)
gets(command);
puts(command);
int pipeIntId;
char whitespaceseparator[2]=" ";
char pidstring [30];
int pid= getpid();
sprintf(pidstring,"%d", pid);
char * whitespace_and_pid;
whitespace_and_pid = strcat(whitespaceseparator,pidstring);
char * command_and_pid;
command_and_pid=strcat(command,whitespace_and_pid); // here's the problem, I guess
if((mkfifo("pipe"/*pipeName*/,0666))==-1)
{
perror("error creating pipe 1");
exit(1);
}
if((pipeIntId=open("pipe",/*pipeName*/O_WRONLY))==-1)
{
perror("error creating pipe 2");
exit(1);
}
int written;
written=write(pipeIntId,command_and_pid,250); // send the command + the pid
close(pipeIntId);
return 0;
}
Решение
Я попробовал ваш код, а также увидеть SegFault на втором strcat()
. Отказ я нашел это command[250]
выделяется сразу после whitespaceseparator[2]
На стеке в моей системе:
(gdb) p &whitespaceseparator
$1 = (char (*)[2]) 0xbf90acd4
(gdb) p &command
$2 = (char (*)[250]) 0xbf90acd6
например (здесь command
начинается "foo..."
), все вкладываются так:
whitespaceseparator
|
| command
| |
v v
+---+---+---+---+---+---+---+---+
|' '| 0 |'f'|'o'|'o'|'.'|'.'|'.'| ...
+---+---+---+---+---+---+---+---+
Я не могу гарантировать, что то же самое происходит в вашей системе (расположение местных жителей на стеке может варьироваться даже между разными версиями одного и того же компилятора), но кажется вероятным. По моему, вот именно то, что происходит:
Как другие сказали, strcat()
Добавляет вторую строку в первый (и результат будет равен первым аргументом). Итак, первый strcat()
переполнение whitespaceseparator[]
(и возвращает whitespaceseparator
в виде whitespace_and_pid
):
+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'| 0 |'.'|'.'| ...
+---+---+---+---+---+---+---+---+
Второй strcat()
пытается добавить whitespace_and_pid
(== whitespaceseparator
) в строку в command
. Отказ Первый символ копии перезаписает завершение 0 строки в command
:
| ===copy===> |
v v
+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'|' '|'.'|'.'| ...
+---+---+---+---+---+---+---+---+
Копия продолжается ...
| ===copy===> |
v v
+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'|' '|'1'|'.'| ...
+---+---+---+---+---+---+---+---+
| ===copy===> |
v v
+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'|' '|'1'|'2'| ...
+---+---+---+---+---+---+---+---+
и будет продолжать копирование " 1234 1234 1234"
... пока он не падает с конца процесса адресного пространства, в какой момент вы получаете SegFault.
Другие советы
strcat
не делает то, что вы думаете. Это модифицирует строку, указанную на свой первый параметр. В этом случае эта строка содержится в 2-байтовом массиве, который, следовательно, переполнен.
Чтобы избежать ошибок переполнения буфера, но используйте strcat
Вы должны использовать strncat
функция.
Ваш звонок может добавить достаточные символы, которые уже вызывают неопределенное поведение примерно в любое время.
whitespaceseparator
Не достаточно большие, чтобы содержать объединенную строку, поэтому вы вызываете неопределенное поведение.
С использованием gets
обычно нахмурился тоже.
strcat
Как правило, небезопасно, потому что он может с радостью перенапряжения буферов, как и в вашем случае.
Прежде всего, whitespaceseparator
только два байта большими? Вы уверены, что вы хотите? И вы объединяете pidstring
к этому? Я думаю, что вы получили аргументы, перепутанные.
В общем, хотя, strcat
Приведет к сбою трудно отладки, если вы не очень осторожны с размерами буферов. Есть более безопасные альтернативы.
«Строкование Concatenation» - это идиома, которую вы должны упасть при изучении C. не только приводит к большому количеству ошибок с переполненными буферами; Это также сверхэффективно. В вашем коде вы могли бы просто включить место в snprintf
Формат строки (вы должны использовать его вместо sprintf
).
По возможности, попробуйте собрать строку полностью за один шаг, используя snprintf
. Отказ Это объединяет всю проверку длины буфера в одно место и делает его действительно трудно ошибиться. Вы также можете позвонить snprintf
Благодаря аргументу размера 0, чтобы получить длину, которую будет объединенная строка, чтобы узнать, какой размер для выделения, если размер выхода не известен заранее (вы должны выделить еще один байт, чем эта длина, чтобы Нулевой терминатор не обрезает ваш выход).