简体   繁体   English

将字符串添加到链表会导致段错误

[英]Adding strings to a linked list result in seg fault

I am trying to add strings that I am reading from a text file to a linked list.我正在尝试将我从文本文件中读取的字符串添加到链接列表中。 Since I don't know how long the file or the string is , I want to do this dynamically.由于我不知道文件或字符串有多长,我想动态地执行此操作。 But somewhere along the line I get a segmentation fault.但是在此过程中的某个地方,我遇到了分段错误。 I have tried everything but I think I overlooked something crucial.我已经尝试了一切,但我认为我忽略了一些关键的东西。 Could someone tell me what I am doing wrong?有人能告诉我我做错了什么吗?

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

typedef struct node {
        char *name;
        int age;
        struct node* next;
    }node;


node *add(node *head, char* n_m){
   node *new_node;
   new_node = (node*)malloc(sizeof(node));
   if(new_node == NULL)
      printf("Fehler bei Speicher reservierung...");
   new_node->name = (char*)malloc(100*sizeof(char));
   if(new_node->name == NULL)
      printf("Fehler bei Speicher reservierung...");

   strcpy(new_node->name, n_m);


   if(head == NULL){
      head = new_node;
      head->next = NULL;
      return head;
   }

   node *current;
   current = head;

   while(current->next != NULL){
      current = current->next;
   }

   current->next = new_node;
   new_node->next = NULL;
   return head;
 }

 void print(node *head){
   node *current;
   current = head;

   while(current != NULL){
     printf("%s\n", current->name);
     current = current->next;
    }
   }

int main(){

  node *head = NULL;

  char character;

  FILE *fp;
  fp = fopen("test.txt", "r");

  while ((character = fgetc(fp)) != EOF) {
  char *n_m;
  n_m = (char*)malloc(100 * sizeof(char));
  if(n_m == NULL)
     printf("Fehler bei Speicher reservierung...");


  int i = 0;
  while (character != ' ') {
     n_m[i++] = character;
     character = fgetc(fp);
  }
  n_m[++i] = '\0';   // NULL-terminate

  head = add(head, n_m);   

  free(n_m);

 }
 print(head);
 return 0;
}

You need to guard against reading beyond the end of file and against going beyond the memory you've allocated.您需要防止读取超出文件末尾和超出您分配的内存。 It may also be possible that your file had a "long line" right at 100 characters by both postfixing and prefixing i you went passed your end of buffer.这也可能是可能是您的文件曾在100个字符双方加上后缀“长行”的权利,并加前缀i你去传递你的缓冲区的末尾。

  while ((character = fgetc(fp)) != EOF) {
      char *n_m;
      n_m = (char*)malloc(100 * sizeof(char));
      if(n_m == NULL)
          printf("Fehler bei Speicher reservierung...");
      int i = 0;
      while ((character != ' ') 
         &&  (character != EOF)
         &&  (i < 100)) {
          n_m[i++] = character;
          character = fgetc(fp);
          }
      // don't postfix add about then prefix add below
      if (i >= 100) {
         … there is a problem …
          }   
      n_m[i] = '\0';   // NULL-terminate
      head = add(head, n_m);
      free(n_m);
      }

You might consider something more like this你可能会考虑更像这样的东西

  #define BUFF_SIZE 100  
  char buff[BUFF_SIZE];
  buff[0] = '\0';
  int i = 0;
  while (((character = fgetc(fp)) != EOF) 
     &&  (i < BUFF_SIZE)) {
      buff[i++] = character;
      character = fgetc(fp);
      if (character = ' ') {
          buff[i] = '\0';   // NULL-terminate
          head = add(head, buff);   
          i = 0;
          buff[0] = '\0';
          }
      }
   if (i >= BUFF_SIZE) { … there is a problem … }

This does a couple of useful things.这做了一些有用的事情。 One is your buffer is statically allocated and it's size is controlled by a single #define.一个是您的缓冲区是静态分配的,它的大小由单个#define 控制。 Second, it reduces the number of loops involved which can improve readability.其次,它减少了可以提高可读性的循环次数。

Your biggest issue is your read of characters does not trap EOF and will continue reading after EOF is encountered, causing i to exceed your array bounds invoking Undefined Behavior leading to your SegFault.您最大的问题是您对字符的读取不会捕获EOF并且会在遇到EOF后继续读取,导致i超出您的数组边界,调用导致您的 SegFault 的未定义行为 The code in question is:有问题的代码是:

  while (character != ' ') {
     n_m[i++] = character;
     character = fgetc(fp);
  }

Since POSIX files do not end with a ' ' (space), but instead a '\\n' , your read of the last word in the file does not stop at EOF .由于 POSIX 文件不以' ' (空格)结尾,而是以'\\n'结尾,因此您对文件中最后一个单词的读取不会在EOF停止。 You further have problems with multiple spaces together repeatedly writing nodes containing the empty-string to your list.您还会遇到多个空格一起重复将包含空字符串的节点写入列表的问题。

You also fail to handle any other whitespace other than space , meaning you are including '\\t' , '\\n' , vertical tab , etc.. within the words you store.您也无法处理除space之外的任何其他空格,这意味着您在存储的单词中包含'\\t''\\n'vertical tab等。 Instead of checking space with ' ' , use the isspace() macro included in ctype.h .不要使用' '检查space ,而是使用ctype.h包含的isspace()宏。

n_m[++i] = '\\0'; should be n_m[i] = '\\0';应该是n_m[i] = '\\0'; . . You increment i with n_m[i++] = character;你用n_m[i++] = character;增加i n_m[i++] = character; . . You do NOT want to increment i again with the pre-increment operator before nul-terminating your string.终止字符串之前,您不想使用预增量运算符再次增加i That results in the last character in the string being indeterminate (again invoking undefined behavior when a read of the string is attempted)这导致字符串中的最后一个字符不确定(在尝试读取字符串时再次调用未定义的行为

Fixing those issues (and using c instead of character , ndx instead of i and buf instead of n_m ), your read and add() to your list would resemble:解决这些问题(并使用c而不是characterndx而不是ibuf而不是n_m ),您的 read 和add()到您的列表将类似于:

    while ((c = fgetc(fp)) != EOF) {            /* read each char in file */
        if (isspace(c) || ndx == MAXC - 1) {    /* is space or buf full? */
            if (in) {                           /* were we within word? */
                buf[ndx] = 0;                   /* nul-terminate */
                head = add (head, buf);         /* add node to list */
            }
            if (ndx < MAXC - 1)     /* buffer not full */ 
                in = 0;             /* set in flag zero */
            ndx = 0;                /* reset index zero */
        }
        else {  /* otherwise */
            buf[ndx++] = c;         /* add char to buf */
            in = 1;                 /* set in flag 1 */
        }
    }

( note: using the variable in as an in/out flag to keep track of whether you are within a word reading characters, or between words reading whitespace solves the problem you would have with multiple whitespace characters in sequences, eg "hello world" ) 注意:使用变量in作为输入in/out标志来跟踪您是在一个单词内读取字符,还是在读取空格的单词之间解决了序列中多个空格字符的问题,例如"hello world"

Optional, but helpful, when allocating nodes that also contain members that need to be allocated is to write a createnode() function that fully Allocates and Initializes all node members rather than putting that code in add() .可选但很有帮助,当分配还包含需要分配的成员的节点时,编写一个createnode()函数来完全分配初始化所有节点成员,而不是将该代码放入add() It keeps things clean and ensures every node you allocate is fully initialized before its use in add() .它使事情保持干净并确保您分配的每个节点在add()使用之前都已完全初始化。 For example:例如:

/** create new node, allocate and initialize all member values,
 *  return pointer to node on success, NULL otherwise.
 */
node *createnode (char *s)
{
    node *new_node;

    new_node = malloc (sizeof *new_node);       /* allocate/validate node */
    if (new_node == NULL) {
        perror ("malloc-Fehler bei Speicher reservierung...");
        return NULL;
    }

    new_node->name = malloc (strlen (s) + 1);   /* allocate/validate name */
    if (new_node->name == NULL) {
        perror ("malloc-Fehler bei Speicher reservierung...");
        return NULL;;
    }

    strcpy (new_node->name, s);     /* initialize all node values */
    new_node->age = 0;
    new_node->next = NULL;

    return new_node;    /* return newly allocated/initialized node */
}

Then your add() function reduces to:然后您的add()函数简化为:

/** add node containing allocated string 'n_m' to list, return pointer
 *  to 1st node in list on success, exit with failure otherwise.
 */
node *add (node *head, char *n_m)
{
    node *new_node = createnode (n_m);  /* allocate/initialize new node */
    node *current = head;               /* pointer to current head */

    if (new_node == NULL)               /* validate allocation */
        exit (EXIT_FAILURE);

    if (!head)                          /* handle 1st node */
        return new_node;

    while (current->next != NULL)       /* iterate to end of list */
        current = current->next;

    current->next = new_node;           /* set next node to new_node */

    return head;                        /* return pointer to head */
}

Putting it altogether and adding a del_list() function to free all allocated memory for the list, you could do the following:把它放在一起并添加一个del_list()函数来释放列表的所有分配内存,您可以执行以下操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXC 1024

typedef struct node {
    char *name;
    int age;
    struct node *next;
} node;

/** create new node, allocate and initialize all member values,
 *  return pointer to node on success, NULL otherwise.
 */
node *createnode (char *s)
{
    node *new_node;

    new_node = malloc (sizeof *new_node);       /* allocate/validate node */
    if (new_node == NULL) {
        perror ("malloc-Fehler bei Speicher reservierung...");
        return NULL;
    }

    new_node->name = malloc (strlen (s) + 1);   /* allocate/validate name */
    if (new_node->name == NULL) {
        perror ("malloc-Fehler bei Speicher reservierung...");
        return NULL;;
    }

    strcpy (new_node->name, s);     /* initialize all node values */
    new_node->age = 0;
    new_node->next = NULL;

    return new_node;    /* return newly allocated/initialized node */
}

/** add node containing allocated string 'n_m' to list, return pointer
 *  to 1st node in list on success, exit with failure otherwise.
 */
node *add (node *head, char *n_m)
{
    node *new_node = createnode (n_m);  /* allocate/initialize new node */
    node *current = head;               /* pointer to current head */

    if (new_node == NULL)               /* validate allocation */
        exit (EXIT_FAILURE);

    if (!head)                          /* handle 1st node */
        return new_node;

    while (current->next != NULL)       /* iterate to end of list */
        current = current->next;

    current->next = new_node;           /* set next node to new_node */

    return head;                        /* return pointer to head */
}

void print (node * head)
{
    node *current;
    current = head;

    while (current != NULL) {
        printf ("%s\n", current->name);
        current = current->next;
    }
}

/** delete all nodes in list */
void del_list (node *head)
{
    node *pn = head;                /* pointer to iterate */

    while (pn) {                    /* iterate over each node */
        node *victim = pn;          /* set victim to current */
        pn = pn->next;              /* advance pointer to next */
        free (victim->name);        /* free current string */
        free (victim);              /* free current node */
    }
}

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

    char buf[MAXC];
    int c = 0, in = 0, ndx = 0;
    node *head = NULL;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while ((c = fgetc(fp)) != EOF) {            /* read each char in file */
        if (isspace(c) || ndx == MAXC - 1) {    /* is space or buf full? */
            if (in) {                           /* were we within word? */
                buf[ndx] = 0;                   /* nul-terminate */
                head = add (head, buf);         /* add node to list */
            }
            if (ndx < MAXC - 1)     /* buffer not full */ 
                in = 0;             /* set in flag zero */
            ndx = 0;                /* reset index zero */
        }
        else {  /* otherwise */
            buf[ndx++] = c;         /* add char to buf */
            in = 1;                 /* set in flag 1 */
        }
    }
    if (fp != stdin)        /* close file if not stdin */
        fclose (fp);

    print (head);           /* print list */
    del_list (head);        /* free all allocated memory */
}

Example Input File示例输入文件

$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.

Example Use/Output示例使用/输出

$ ./bin/ll_name_age dat/captnjack.txt
This
is
a
tale
Of
Captain
Jack
Sparrow
A
Pirate
So
Brave
On
the
Seven
Seas.

Memory Use/Error Check内存使用/错误检查

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.在你写的,可动态分配内存的任何代码,您有任何关于分配的内存任何块2个职责:(1)始终保持一个指针的起始地址的存储器中,以便块,(2),当它是没有它可以被释放不再需要。

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的范围,尝试读取或基于未初始化值的条件跳转,最后确认你释放了你分配的所有内存。

For Linux valgrind is the normal choice.对于 Linux valgrind是正常的选择。 There are similar memory checkers for every platform.每个平台都有类似的内存检查器。 They are all simple to use, just run your program through it.它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/ll_name_age dat/captnjack.txt
==18265== Memcheck, a memory error detector
==18265== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18265== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18265== Command: ./bin/ll_name_age dat/captnjack.txt
==18265==
This
is
a
tale
Of
Captain
Jack
Sparrow
A
Pirate
So
Brave
On
the
Seven
Seas.
==18265==
==18265== HEAP SUMMARY:
==18265==     in use at exit: 0 bytes in 0 blocks
==18265==   total heap usage: 35 allocs, 35 frees, 6,132 bytes allocated
==18265==
==18265== All heap blocks were freed -- no leaks are possible
==18265==
==18265== For counts of detected and suppressed errors, rerun with: -v
==18265== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.始终确认您已释放所有分配的内存并且没有内存错误。

Look things over and let me know if you have further questions.仔细检查一下,如果您还有其他问题,请告诉我。

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

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