简体   繁体   English

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

[英]Segmentation fault 11 printing string from struct

This is the first time I've run into Segmentation fault 11 in C and I can't seem to wrap my head around what is actually going wrong. 这是我第一次遇到C语言中的Segmentation Fault 11,我似乎无法全神贯注于实际出了什么问题。

What I'm trying to do is write a few int values to a struct plus the file name from the command line (char *) from a child process and then write the struct to the pipe to read from from the parent process. 我想做的是将一些int值写入子结构,再加上子进程从命令行(char *)的文件名,然后将结构写入管道以从父进程中读取。 It works fine when it's only the integers and I take out the code working with the string, but once I add in the string and try to print out the file name in the parent process I get the segmentation fault 11 when the program is run. 当它仅是整数并且我取出使用该字符串的代码时,它可以正常工作,但是一旦我添加了该字符串并尝试在父进程中打印出文件名,则在运行该程序时出现了分段错误11。

I've looked at various posts from all over, but have noticed that the common issue for this is when someone attempts to assign a string to a char array and prints, but I made sure to use only char * here. 我看过来自各地的各种文章,但是注意到,常见的问题是有人尝试将字符串分配给char数组并打印时,但是我确保在这里仅使用char *。 Here's the code where it locks up 这是锁定的代码

 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

Here is the read out of what the program does when I run it: 这是我运行程序时读取的内容:

$ ./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

Also here is the full code EDIT: I swapped out the code using sizeof for string and used strlen 这也是完整的代码编辑:我用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);
}

I really appreciate any insight on this! 我对此表示感谢!

Your main problem is that you don't quite have the right understanding of C strings. 您的主要问题是您对C字符串的理解不正确。 You cannot do sizeof(char_pointer) . 您不能执行sizeof(char_pointer) That will just give you the pointer size (4 in a 32 bit system) and not the size of the string it points to. 这只会给您指针大小(在32位系统中为4),而不是它指向的字符串的大小。 Use strlen to get the length of a string. 使用strlen获取字符串的长度。

The second related problem is that you are writing a pointer address, v.file , and not the full string contents through the pipe. 第二个相关问题是您正在通过管道写入指针地址v.file ,而不是完整的字符串内容。 That is not correct because each process has a seperate address space and hence a pointer in one process is not valid in another process. 这是不正确的,因为每个进程都有单独的地址空间,因此一个进程中的指针在另一进程中无效。

There are several ways to fix your problem. 有几种方法可以解决您的问题。 I will give you the simplest (but not the best). 我会给你最简单的(但不是最好的)。

First declare file inside the struct as a char array rather than a char pointer. 首先将结构内部的file声明为char数组而不是char指针。 This essentially gives you a fixed sized buffer. 这实际上为您提供了固定大小的缓冲区。

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

Then remove the malloc call. 然后删除malloc调用。 You don't need it anymore as file is already a buffer that you can copy into. 您不再需要它,因为file已经是可以复制到其中的缓冲区。

Finally, make sure you don't overflow the buffer during string copy: 最后,请确保在字符串复制期间不会使缓冲区溢出:

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

You also don't need the +1 in the write as the sizeof gives you the full buffer size. 您也不需要write +1 ,因为sizeof提供完整的缓冲区大小。

I'll leave it as an exercise for you to use dynamic memory for the file name in the struct. 我将保留它作为练习,让您使用动态内存作为结构体中的文件名。 It's not hard but will require you to change your read and write logic a little as you will need to read/write the file name seperately (because writing the whole struct in that case will just write the pointer not the contents). 这并不难,但是由于您需要分别读取/写入文件名,因此需要稍微更改读取和写入逻辑(因为在这种情况下写入整个结构只会写入指针而不是内容)。

There's a few things wrong here. 这里有些错误。 First, you aren't free() ing the space you allocate with malloc() . 首先,您无法使用malloc() free()分配的空间。

Second, you should be using strlen() in place of sizeof() in your calculations. 其次,您应该在计算中使用strlen()代替sizeof() This occurs twice in your code. 这在您的代码中发生两次。

Third, the declaration char fname = "Not set"; 第三,声明char fname = "Not set"; is not safe, since it is actually a const char* to read-only memory (text segment), and it's later pointed to something allocated via malloc() . 这是不安全的,因为它实际上是只读内存(文本段)的const char* ,并且稍后指向通过malloc()分配的内容。 Don't do this. 不要这样

Corrected Code Listing 更正的代码清单


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

Sample Run 样品运行


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

This code has several issues which for some reason were not mentioned. 该代码有几个由于某种原因未提及的问题。 Hence here goes my take. 因此,这就是我的看法。

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

Well, gcc -Wall -Wextra tells me: 好吧,gcc -Wall -Wextra告诉我:

warning: implicit declaration of function 'wait' 警告:函数“ wait”的隐式声明

How are you compiling this? 您如何编译呢? Did you see this error and ignored it? 您看到此错误并忽略了吗? If so, no candy for a week. 如果是这样,则一周不吃糖果。

void error_exit(char *);

typedef struct total {

  int words, lines, bytes;
  char *file;

} Vals;

Weird naming. 奇怪的命名。 'total'? '总'? 'vals'? 'vals'?

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

  int pd[2]; //pipe descriptor

Rather useless comment. 而是无用的评论。

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

  if(argc > 1)

Should test argc == 2 and throw insults if > 2. 应该测试argc == 2并在> 2时抛出侮辱。

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

Incorrect. 不正确 strlen return the length without the terminating null character . strlen返回没有终止空字符的长度。 Consider using strdup instead (non-standard). 考虑改用strdup(非标准)。 Missing NULL check. 缺少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]);

You typically close earlier. 您通常会更早关闭。

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

This code this does not work, but you may be tempted to use 'char file[BIGNUM];' 这段代码不起作用,但是您可能会想使用'char file [BIGNUM];' mentioned in other comments, so let's steal a sample which was supposed to work: 在其他评论中提到过,因此让我们窃取一个应该起作用的示例:

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

Incorrect. 不正确 Let's assume this adds up to the size of the structure - then '+1' found here causes reading 1 byte after the structure. 假设这加起来就是结构的大小-然后在这里找到的“ +1”会导致在结构之后读取1个字节。 But sizes of all struct elements are not guaranteed to add up to the size of the entire structure due to padding. 但是由于填充,不能保证所有结构元素的大小加起来等于整个结构的大小。 If using 'char file[BIGNUM];' 如果使用“ char file [BIGNUM];” just sizeof(v). 只是sizeof(v)。 If playing with char *file, you have to make sure file is always last and for simplicity just use offsetof to the file pointer. 如果使用char * file进行播放,则必须确保文件始终是最后一个,为简单起见,只需将offsetof用作文件指针即可。

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

Incorrect. 不正确 Should use _Exit(2) instead. 应该使用_Exit(2)代替。

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

What's up with the else clause which only prints something and passes execution below? else子句怎么处理,该子句仅打印内容并在下面传递执行?

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

Incorrect. 不正确 wait can return due to a signal. 等待会由于信号而返回。

  close(pd[1]);

Should close before wait. 应在等待之前关闭。

  //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 does not have 2048 bytes, so this can happen to work only by accident. 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);
}

Consider using perror or err-family of functions (not portable). 考虑使用错误或错误的功能家族(不可移植)。

Finally, I recommend finding less atrocious style (from linux or KNF). 最后,我建议您查找不太恶劣的样式(从linux或KNF)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM