分段错误11从struct打印字符串

时间:2015-05-07 00:17:10

标签: c string struct pipe fork

这是我第一次遇到C中的Segmentation fault 11,我似乎无法绕过实际出错的地方。

我想要做的是将一些int值写入一个struct加上来自子进程的命令行(char *)的文件名,然后将该struct写入管道以从中读取父进程。当它只有整数并且我取出使用字符串的代码时工作正常,但是一旦我添加了字符串并尝试在父进程中打印出文件名,我就会得到分段错误11程序运行。

我已经查看过来自各地的各种帖子,但是注意到这个问题的常见问题是有人试图将字符串分配给char数组并进行打印,但我确保在这里只使用char * 。这是锁定的代码

 if((read(pd[0], &pv, 2048)) == -1)
 {
   error_exit("read not working");
 }

 printf("words = %d\n", pv.words);
 printf("lines = %d\n", pv.lines);
 printf("bytes = %d\n", pv.bytes);
 printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line

以下是我运行程序时所读取的内容:

$ ./a testfile
Parent process... should be waiting on child...
In child process! pid = 21993  
it worked? testfile
Done with child process!
words = 1
lines = 2
bytes = 3
Segmentation fault: 11

此处还有完整的代码 编辑:我使用sizeof for string换掉了代码并使用了strlen

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

void error_exit(char *);

typedef struct total {

  int words, lines, bytes;
  char *file;

} Vals;

int main(int argc, char *argv[])
{

  int pd[2]; //pipe descriptor
  pid_t pid;
  Vals v, pv;
  char *fname = "Not set";

  if(argc > 1)
  {
    fname = malloc(strlen(argv[1]));
    strcpy(fname, argv[1]);
  }

  if((pipe(pd)) == -1)
  {
    error_exit("pipe creation");
  }

  if((pid = fork()) == -1)
  { 
    error_exit("the fork forked up!");
  }
  else if(pid == 0)
  {

    printf("In child process! pid = %d\n", getpid());
    v.words = 1;
    v.lines = 2;
    v.bytes = 3;
    v.file = malloc(strlen(fname));
    strcpy(v.file, fname);
    printf("it worked? %s\n", v.file);

    close(pd[0]);

    if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
    {
      error_exit("Write from child");
    }

    //return; //return from child
    printf("Done with child process!\n");
    close(pd[1]);
    return 0;
  }
  else
  {
    printf("Parent process... should be waiting on child...\n");
  }
  //wait for child
  while((pid = wait(NULL)) > 0);

  close(pd[1]);

  //Vals pv = {0, 0, 0, "pv.file not set"};

  //just assign anything to file to see if it fixes
  //pv.file = malloc(strlen(fname));

  if((read(pd[0], &pv, 2048)) == -1)
  {
    error_exit("read not working");
  }

  printf("words = %d\n", pv.words);
  printf("lines = %d\n", pv.lines);
  printf("bytes = %d\n", pv.bytes);
  printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line

  close(pd[0]);

  //program ended normally
  return 0;

}

void error_exit(char *err)
{
  printf("exiting because of this section: %s\nerrno = %d", err, errno);
  exit(1);
}

我非常感谢您对此有任何见解!

3 个答案:

答案 0 :(得分:4)

你的主要问题是你对C字符串没有正确的理解。你做不到sizeof(char_pointer)。这只会给你指针大小(32位系统中的4)而不是它指向的字符串的大小。使用strlen获取字符串的长度。

第二个相关问题是您正在编写指针地址v.file,而不是通过管道写入完整的字符串内容。这是不正确的,因为每个进程都有一个单独的地址空间,因此一个进程中的指针在另一个进程中无效。

有几种方法可以解决您的问题。我会给你最简单的(但不是最好的)。

首先在结构中声明file作为char数组而不是char指针。这基本上为您提供了固定大小的缓冲区。

#define MAX_FILENAME_LEN 64
typedef struct total {
  int words, lines, bytes;
  char file[MAX_FILENAME_LEN];
} Vals;

然后删除malloc调用。您不再需要它,因为file已经是您可以复制到的缓冲区。

最后,确保在字符串复制期间不要溢出缓冲区:

if (strlen(fname) >= MAX_FILENAME_LEN) {
    error_exit("File name too long");
}
strcpy(v.file, fname);

您也不需要+1中的write,因为sizeof会为您提供完整的缓冲区大小。

我将把它作为练习留给你使用动态内存作为结构中的文件名。它并不难,但需要你稍微改变你的读写逻辑,因为你需要单独读/写文件名(因为在这种情况下写整个结构只会写指针而不是内容)

答案 1 :(得分:1)

这里有一些问题。首先,您不是free()使用malloc()分配的空间。

其次,您应该在计算中使用strlen()代替sizeof()。这在代码中出现两次。

第三,声明char fname = "Not set";不安全,因为它实际上是const char*只读内存(文本段),后来指向通过malloc()分配的内容。不要这样做。

更正代码清单

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define MAX_BUF_LEN (1024)

void error_exit(char *);

typedef struct total {

    int words, lines, bytes;
    char file[MAX_BUF_LEN];

} Vals;

int main(int argc, char *argv[])
{
    int pd[2]; //pipe descriptor
    pid_t pid;
    Vals v, pv;
    char fname[MAX_BUF_LEN] = "Not set";

    if(argc > 1) {
        //fname = malloc(sizeof(argv[1]) + 1);
        //fname = argv[1];
        strcpy(fname, argv[1]);
    }

    if((pipe(pd)) == -1) {
        error_exit("pipe creation");
    }

    if((pid = fork()) == -1) { 
        error_exit("the fork forked up!");
    } else if(pid == 0) {
        printf("In child process! pid = %d\n", getpid());
        v.words = 1;
        v.lines = 2;
        v.bytes = 3;
        //v.file = malloc(strlen(fname) + 1);
        strcpy(v.file, fname);
        printf("it worked? %s\n", v.file);
        close(pd[0]);

        if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
            error_exit("Write from child");
        }

        printf("Done with child process!\n");
        close(pd[1]);
        return 0; //return from child
    }
    else
    {
        printf("Parent process... should be waiting on child...\n");
    }
    //wait for child
    while((pid = wait(NULL)) > 0);

    close(pd[1]);

    if((read(pd[0], &pv, 2048)) == -1) {
        error_exit("read not working");
    }

    printf("words = %d\n", pv.words);
    printf("lines = %d\n", pv.lines);
    printf("bytes = %d\n", pv.bytes);
    printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line

    close(pd[0]);

    //program ended normally
    return 0;

}

void error_exit(char *err)
{
    printf("exiting because of this section: %s\nerrno = %d", err, errno);
    exit(1);
}

示例运行

Parent process... should be waiting on child...
In child process! pid = 7410
it worked? HelloWorld
Done with child process!
words = 1
lines = 2
bytes = 3
file = HelloWorld

答案 2 :(得分:0)

此代码有几个问题,由于某些原因未提及。因此,这是我的看法。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

嗯,gcc -Wall -Wextra告诉我:

  

警告:隐式声明函数'wait'

你是如何编译的?你看到这个错误而忽略了吗?如果是这样,一周没有糖果。

void error_exit(char *);

typedef struct total {

  int words, lines, bytes;
  char *file;

} Vals;

奇怪的命名。 &#39;总&#39 ;? &#39;瓦尔斯&#39;?

int main(int argc, char *argv[])
{

  int pd[2]; //pipe descriptor

相当无用的评论。

  pid_t pid;
  Vals v, pv;
  char *fname = "Not set";

  if(argc > 1)

如果&gt;测试argc == 2并抛出侮辱2。

  {
    fname = malloc(strlen(argv[1]));
    strcpy(fname, argv[1]);

不正确的。 strlen返回长度而没有终止空字符。考虑使用strdup(非标准)。缺少NULL检查。

  }

  if((pipe(pd)) == -1)
  {
    error_exit("pipe creation");
  }

  if((pid = fork()) == -1)
  { 
    error_exit("the fork forked up!");
  }
  else if(pid == 0)
  {

    printf("In child process! pid = %d\n", getpid());
    v.words = 1;
    v.lines = 2;
    v.bytes = 3;
    v.file = malloc(strlen(fname));
    strcpy(v.file, fname);
    printf("it worked? %s\n", v.file);

    close(pd[0]);

您通常会提早关闭。

    if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + strlen(v.file))) == -1)
    {
      error_exit("Write from child");
    }

此代码不起作用,但您可能想要使用&#39; char文件[BIGNUM];&#39;在其他评论中提到,所以让我们偷一个应该有用的样本:

    if((write(pd[1], &v, sizeof(v.words) + sizeof(v.lines) + sizeof(v.bytes) + sizeof(v.file) + 1)) == -1) {
        error_exit("Write from child");
    }

不正确的。让我们假设这会增加结构的大小 - 然后&#39; + 1&#39;在这里找到导致结构后读取1个字节。但是由于填充,不能保证所有struct元素的大小总和整个结构的大小。如果使用&#39; char文件[BIGNUM];&#39;只是sizeof(v)。如果使用char *文件,你必须确保文件始终是最后的,为简单起见,只需将offsetof用于文件指针。

    //return; //return from child
    printf("Done with child process!\n");
    close(pd[1]);
    return 0;

不正确的。应该使用_Exit(2)代替。

  }
  else
  {
    printf("Parent process... should be waiting on child...\n");
  }

使用else子句,只打印一些内容并在下面执行执行是什么?

  //wait for child
  while((pid = wait(NULL)) > 0);

不正确的。等待可以由于信号而返回。

  close(pd[1]);

等待之前应该关闭。

  //Vals pv = {0, 0, 0, "pv.file not set"};

  //just assign anything to file to see if it fixes
  //pv.file = malloc(strlen(fname));

  if((read(pd[0], &pv, 2048)) == -1)
  {
    error_exit("read not working");
  }

pv没有2048个字节,所以这可能只是偶然发生。

  printf("words = %d\n", pv.words);
  printf("lines = %d\n", pv.lines);
  printf("bytes = %d\n", pv.bytes);
  printf("file = %s\n", pv.file); //locks up here and gives segmentation fault 11 on the command line

  close(pd[0]);

  //program ended normally
  return 0;

}

void error_exit(char *err)
{
  printf("exiting because of this section: %s\nerrno = %d", err, errno);
  exit(1);
}

考虑使用perror或err-family函数(不可移植)。

最后,我建议找一些不太糟糕的风格(来自linux或KNF)。