1

对这里的第二次调用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;
}
4

7 回答 7

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[](并返回whitespaceseparatorwhitespace_and_pid):

+---+---+---+---+---+---+---+---+
|' '|'1'|'2'|'3'|'4'| 0 |'.'|'.'| ...
+---+---+---+---+---+---+---+---+

第二个strcat()尝试将whitespace_and_pid(== whitespaceseparator) 附加到字符串 at 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"......直到它从进程地址空间的末尾掉下来,此时你会得到一个段错误。

于 2010-11-30T01:29:07.423 回答
3

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

于 2010-11-30T00:50:52.810 回答
2

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

于 2010-11-30T00:52:33.183 回答
1

您的 gets 调用可能已经添加了足够的字符,以便在任何时候导致未定义的行为。

于 2010-11-30T00:50:32.497 回答
1

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

使用gets通常也是不受欢迎的。

于 2010-11-30T00:51:19.003 回答
1

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

首先,whitespaceseparator只有两个字节大吗?你确定那是你想要的吗?你连接pidstring到它?我想你把论点弄混了。

但总的来说,strcat如果您对缓冲区大小不太小心,将导致难以调试的崩溃。有更安全的选择。

于 2010-11-30T00:51:36.973 回答
1

“字符串连接”是学习 C 时应该放弃的习语。它不仅会导致大量缓冲区溢出的错误;它也非常低效。在您的代码中,您可以在snprintf格式字符串中包含空格(您应该使用它来代替sprintf)。

只要有可能,请尝试使用snprintf. 这将所有缓冲区长度检查合并到一个地方,并且很难出错。如果事先不知道输出的大小,您还可以snprintf使用 0 大小参数调用以获取组合字符串的长度,以便找出要分配的大小(您应该比这多分配一个字节长度,以便空终止符不会截断您的输出)。

于 2010-11-30T01:37:16.830 回答