【问题标题】:Segmentation fault 11 printing string from struct结构中的分段错误 11 打印字符串
【发布时间】:2015-07-17 08:42:31
【问题描述】:

这是我第一次在 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 换出代码 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);
}

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

【问题讨论】:

  • 我认为你想要strlen(v.file) 而不是sizeof(v.file)
  • 哦,是的,这让生活变得更轻松 :) 我在整个代码中都进行了更改,但仍然出现分段错误。我确实注意到,如果我做 pv.file = malloc(strlen(fname)); 在从管道读取父级之前那么它不会给我分段错误,但它会打印出一个空字符串。
  • 另外,sizeof(argv[1]),同样的问题。而fname = argv[1]; 也可能不会做你想做的事。试试strcpy
  • 不要通过管道发送指针;它们在其他虚拟地址空间上毫无用处
  • 另外,为了方便调试,我会保存 write() 和 read() 的返回值(它们包含实际写入和读取的字节数)并打印出来,

标签: c string struct pipe fork


【解决方案1】:

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

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

奇怪的命名。 '全部的'? '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 返回长度没有终止空字符。考虑使用 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];'在其他 cmets 中提到过,所以让我们偷一个应该可以工作的样本:

    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”会导致在结构之后读取 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);

不正确。由于信号,wait 可以返回。

  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)。

【讨论】:

    【解决方案2】:

    这里有一些问题。首先,你不是free()ing 分配给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
    

    【讨论】:

    • 嘿,非常感谢!解释真的很有帮助!我知道我错过了一些关于 C 字符串的东西。我有一段时间没有使用 malloc 并且忘记了有关该功能的基础知识。我得再读一遍。
    • @FrankA。没问题。另外,请阅读为什么strncpystrcpy 更好,然后为什么您可能甚至不想使用strncpy,而是尽可能使用strlcpy。如果你问我,我可以离线告诉你更多。 randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already
    【解决方案3】:

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

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

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

    首先在 struct 中声明 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);
    

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

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

    【讨论】:

    • 嘿,谢谢你的回答!那肯定成功了。我知道我应该在此之前复习一下 C 弦。不过,我真的很感谢他们的解释!这帮助很大!
    猜你喜欢
    • 2019-11-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 2015-01-18
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多