分段错误 11 从结构打印字符串

Segmentation fault 11 printing string from struct

这是我第一次 运行 进入 C 语言中的分段错误 11,我似乎无法理解到底出了什么问题。

我想做的是将几个 int 值写入一个结构加上来自子进程的命令行 (char *) 的文件名,然后将结构写入管道以从父进程。当它只是整数并且我取出使用字符串的代码时它工作正常,但是一旦我添加了字符串并尝试在父进程中打印出文件名我得到分段错误 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 换出了字符串的代码并使用了 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);
}

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

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

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

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

首先在结构中声明file为一个字符数组而不是一个字符指针。这实际上为您提供了一个固定大小的缓冲区。

#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);

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

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

这里有些地方不对。首先,您没有 free() 使用 malloc() 分配的 space。

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

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

更正代码清单


#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

这段代码有几个问题,出于某种原因没有提到。因此,这是我的看法。

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

嗯,gcc -Wall -Wextra 告诉我:

warning: implicit declaration of function ‘wait’

你是如何编译这个的?您是否看到此错误并忽略了它?如果是这样,一周不吃糖。

void error_exit(char *);

typedef struct total {

  int words, lines, bytes;
  char *file;

} Vals;

奇怪的命名。 'total'? '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)

应该测试 argc == 2 并在 > 2 时进行侮辱。

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

不正确。 strlen return 没有终止空字符 的长度。考虑改用 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");
    }

这段代码不起作用,但您可能会想使用其他评论中提到的 'char file[BIGNUM];',所以让我们窃取一个应该起作用的示例:

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

不正确。假设这加起来等于结构的大小 - 那么此处找到的“+1”导致读取结构 after 的 1 个字节。但是由于填充,不能保证所有结构元素的大小加起来等于整个结构的大小。如果使用 'char file[BIGNUM];' 只是 sizeof(v)。如果使用 char *file,你必须确保文件总是最后一个,为了简单起见,只需对文件指针使用 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);

不正确。由于有信号,可以等待 return。

  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 系列函数(不可移植)。

最后,我建议找到不那么残暴的风格(来自 linux 或 KNF)。