2010-11-30 110 views
1

第二次调用strcat这里是产生分段错误,为什么?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; 
} 
+0

我解决了这个问题,使用第一个答案在这里http://stackoverflow.com/questions/308695/c-string-concatenation – andandandand 2010-11-30 01:20:07

回答

4

我试过了你的代码,并且在第二个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()尝试在command追加whitespace_and_pid(== whitespaceseparator)到该字符串。副本的第一个字符将在command覆盖串的终止0:

| ===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" ......直到它脱落进程地址空间的结尾,此时您将收到段错误。

1

您的获取调用可能已添加足够多的字符,在任何时候都会导致未定义的行为。

3

strcat不符合您的想法。它修改第一个参数指向的字符串。在这种情况下,该字符串包含在一个2字节的数组中,因此该字符串溢出。

+0

哦,我从这里http://www.thinkage.ca/english/gcos /expl/c/lib/strcat.html。我很好奇它为什么它第一次运行。 – andandandand 2010-11-30 01:19:22

+0

我不认为它在第一次运行时“工作”:将`pidstring`复制到`whitespaceseparator`的末尾是未定义的行为,这并不意味着它必然会立即崩溃。马修的回答有一个非常合理的解释,为什么你会看到你所表示的一个崩溃,假设你的实现和他的一样。 – 2010-11-30 02:26:04

+0

不,我不是说它使用这种带有空白分隔符的pidstring的情况。它在第一次使用足够大小的缓冲区时运行,在这种情况下将whitespaceseparator的大小更改为较大的值也不会使其工作。 – andandandand 2010-11-30 03:29:50

1

whitespaceseparator不足以包含连接的字符串,所以导致未定义的行为。

使用gets通常也被忽视。

1

strcat通常是不安全的,因为它可以愉快地溢出缓冲区,就像它在你的情况一样。

首先,whitespaceseparator只有两个字节大?你确定这就是你想要的吗?你连接pidstring它?我认为你的观点混淆了。

一般来说,如果你对缓冲区大小不是很小心的话,strcat会导致难以调试的崩溃。有更安全的选择。

2

为避免缓冲区溢出错误,但使用strcat您应该使用strncat函数。

1

“String concatenation”是学习C时应该放弃的习惯用法。不仅会导致大量带有溢出缓冲区的错误;它也是超效率的。在你的代码中,你可以只包含snprintf格式字符串中的空格(你应该用它来代替sprintf)。

只要有可能,尝试使用snprintf在一个步骤中完全组装字符串。这将所有的缓冲区长度检查合并到一个地方,并且很难弄错。您也可以调用snprintf,使用0大小的参数来获取组合字符串的长度,以便找出要分配的大小,如果输出的大小未预先知道(您应该再分配一个字节这个长度使空终止符不会截断你的输出)。