分段故障 - 动态内存分配

时间:2014-05-06 19:00:26

标签: c

我一直有这个问题围绕着我已经给予的一些代码。这个问题在menu_add_book函数或read_line函数中的某个地方,当我运行它时它编译没有问题我可以使用输入文件输入第一个选项,但是当涉及到标题时出现分段错误。我很确定这与分配内存有关,但是对于我的生活无法解决什么问题,如果有人可以帮我解释一下,那么你将成为一个生命保护者。 我的代码如下

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

#define MAX_TITLE_LENGTH  100
#define MAX_AUTHOR_LENGTH 100

/* Book structure */

struct Book
{
   /* Book details */
   char title[MAX_TITLE_LENGTH+1];   /* name string */
   char author[MAX_AUTHOR_LENGTH+1]; /* job string */
   int  year;                        /* year of publication */

   /* pointers to left and right branches pointing down to next level in
      the binary tree */
   struct Book *left, *right;
};

/* tree of books, initialized to NULL. */
static struct Book *book_tree = NULL;

/* read_line():
 *
 * Read line of characters from file pointer "pf", copying the characters
 * into the "line" string, up to a maximum of "max_length" characters, plus
 * one for the string termination character '\0'. Reading stops upon
 * encountering the end-of-line character '\n', for which '\0' is substituted
 * in the string. If the end of file character EOF is reached before the end
 * of the line, the failure condition (-1) is returned. If the line is longer
 * than the maximum length "max_length" of the string, the extra characters
 * are read but ignored. Success is returned (0) on successfully reading
 * a line.
 */
static int read_line ( FILE *pf, char *line, int max_length )
{
   int i;
   char ch;

   /* initialize index to string character */
   i = 0;

   /* read to end of line, filling in characters in string up to its
      maximum length, and ignoring the rest, if any */
   for(;;)
   {
      /* read next character */
      ch = fgetc(pf);

      /* check for end of file error */
      if ( ch == EOF )
     return -1;

      /* check for end of line */
      if ( ch == '\n' )
      {
     /* terminate string and return */
     line[i] = '\0';
     return 0;
      }

      /* fill character in string if it is not already full*/
      if ( i < max_length )
     line[i++] = ch;
   }

   /* the program should never reach here */
   return -1;
}

/* read_string():
 *
 * Reads a line from the input file pointer "pf", starting with the "prefix"
 * string, and filling the string "string" with the remainder of the contents
 * of the line. If the start of the line does not match the "prefix" string,
 * the error condition (-1) is returned. Having read the prefix string,
 * read_string() calls read_line() to read the remainder of the line into
 * "string", up to a maximum length "max_length", and returns the result.
 */
static int read_string ( FILE *pf,
             char *prefix, char *string, int max_length )
{
   int i;

   /* read prefix string */
   for ( i = 0; i < strlen(prefix); i++ )
      if ( fgetc(pf) != prefix[i] )
     /* file input doesn't match prefix */
     return -1;

   /* read remaining part of line of input into string */
   return ( read_line ( pf, string, max_length ) );
}

/* menu_add_book():
 *
 * Add new book to database
 */
static void menu_add_book(void)
{
FILE *pf = NULL;
char *line = NULL; 
int max_length = 100;
int year;
struct Book *new = NULL;
new = (struct Book *) malloc ( sizeof(struct Book) );
fprintf(stderr,"\nEnter Title:");
read_line ( pf, line, max_length );
fprintf(stderr,"\nEnter Author:");
read_line ( pf, line, max_length );
fprintf(stderr,"\nEnter Year:\n");
scanf( "%i", &year);
}

/* menu_print_database():
 *
 * Print database of books to standard output in alphabetical order of title.
 */
static void menu_print_database(void)
{
   /* fill in the code here in part 1, and add any extra functions you need */
}       

/* menu_get_book_details():
 *
 * Get details of book from database.
 */
static void menu_get_book_details(void)
{
   /* fill in the code here in part 2, and add any extra functions you need */
}

/* menu_delete_book():
 *
 * Delete new book from database.
 */
static void menu_delete_book(void)
{
   /* fill in the code here in part 2, and add any extra functions you need */

}

/* read file containing database of books */
static void read_book_database ( char *file_name )
{
   /* fill in the code here in part 3, and add any extra functions you need */
}

/* get_tree_depth():
 *
 * Recursive function to compute the number of levels in a binary tree.
 */
static int get_tree_depth ( struct Book *book, int level )
{
   int level1, level2;

   /* return with the current level if we've reached the bottom of this
      branch */
   if ( book == NULL ) return level;

   /* we need to go to the next level down */
   level++;

   /* count the number of levels down both branches */
   level1 = get_tree_depth ( book->left,  level );
   level2 = get_tree_depth ( book->right, level );

   /* return the depth of the deepest branch */
   if ( level1 > level2 ) return level1;
   else return level2;
}

/* menu_print_tree():
 *
 * Print tree to standard output. You can use this function to print out the
 * tree structure for debugging purposes. It is also used by the testing
 * software to check that the tree is being built correctly.
 *
 * The first letter of the title of each book is printed.
 */
static void menu_print_tree(void)
{
   int no_levels, level, size, i, j, k;
   struct Book **row;

   /* find level of lowest node on the tree */
   no_levels = get_tree_depth ( book_tree, 0 );

   /* abort if database is empty */
   if ( no_levels == 0 ) return;

   /* compute initial indentation */
   assert ( no_levels < 31 );

   row = (struct Book **) malloc((1 << (no_levels-1))*sizeof(struct Book *));
   row[0] = book_tree;
   printf ( "\n" );
   for ( size = 1, level = 0; level < no_levels; level++, size *= 2 )
   {
      /* print books at this level */
      for ( i = 0; i < size; i++ )
      {
     if ( i == 0 )
        for ( j = (1 << (no_levels - level - 1)) - 2; j >= 0; j-- )
           printf ( " " );
     else
        for ( j = (1 << (no_levels - level)) - 2; j >= 0; j-- )
           printf ( " " );

     if ( row[i] == NULL )
        printf ( " " );
         else
        printf ( "%c", row[i]->title[0] );
      }

      printf ( "\n" );

      if ( level != no_levels-1 )
      {
     /* print connecting branches */
     for ( k = 0; k < ((1 << (no_levels - level - 2)) - 1); k++ )
     {
        for ( i = 0; i < size; i++ )
        {
           if ( i == 0 )
          for ( j = (1 << (no_levels - level - 1))-3-k; j >= 0; j-- )
             printf ( " " );
           else
          for ( j = (1 << (no_levels - level)) - 4 - 2*k; j >= 0; j-- )
             printf ( " " );

           if ( row[i] == NULL || row[i]->left == NULL )
          printf ( " " );
           else
          printf ( "/" );

           for ( j = 0; j < 2*k+1; j++ )
          printf ( " " );

           if ( row[i] == NULL || row[i]->right == NULL )
          printf ( " " );
           else
          printf ( "\\" );
        }

        printf ( "\n" );
     }

     /* adjust row of books */
     for ( i = size-1; i >= 0; i-- )
     {
        row[2*i+1] = (row[i] == NULL) ? NULL : row[i]->right;
        row[2*i]   = (row[i] == NULL) ? NULL : row[i]->left;
     }
      }
   }

   free(row);
}       

/* codes for menu */
#define ADD_CODE     0
#define DETAILS_CODE 1
#define DELETE_CODE  2
#define PRINT_CODE   3
#define TREE_CODE    4
#define EXIT_CODE    5

int main ( int argc, char *argv[] )
{
   /* check arguments */
   if ( argc != 1 && argc != 2 )
   {
      fprintf ( stderr, "Usage: %s [<database-file>]\n", argv[0] );
      exit(-1);
   }

   /* read database file if provided, or start with empty database */
   if ( argc == 2 )
      read_book_database ( argv[1] );

   for(;;)
   {
      int choice, result;
      char line[301];             

      /* print menu to standard error */
      fprintf ( stderr, "\nOptions:\n" );
      fprintf ( stderr, "%d: Add new book to database\n",      ADD_CODE );
      fprintf ( stderr, "%d: Get details of book\n",       DETAILS_CODE );
      fprintf ( stderr, "%d: Delete book from database\n",  DELETE_CODE );
      fprintf ( stderr, "%d: Print database to screen\n",    PRINT_CODE );
      fprintf ( stderr, "%d: Print tree\n",                   TREE_CODE );
      fprintf ( stderr, "%d: Exit database program\n",        EXIT_CODE );
      fprintf ( stderr, "\nEnter option: " );

      if ( read_line ( stdin, line, 300 ) != 0 ) continue;

      result = sscanf ( line, "%d", &choice );
      if ( result != 1 )
      {
     fprintf ( stderr, "corrupted menu choice\n" );
     continue;
      }

      switch ( choice )
      {
         case ADD_CODE: /* add book to database */
     menu_add_book();
     break;

         case DETAILS_CODE: /* get book details from database */
     menu_get_book_details();
     break;

         case DELETE_CODE: /* delete book from database */
     menu_delete_book();
     break;

         case PRINT_CODE: /* print database contents to screen
                 (standard output) */
     menu_print_database();
     break;

         case TREE_CODE: /* print tree to screen (standard output) */
     menu_print_tree();
     break;

     /* exit */
         case EXIT_CODE:
     break;

         default:
     fprintf ( stderr, "illegal choice %d\n", choice );
     break;
      }

      /* check for exit menu choice */
      if ( choice == EXIT_CODE )
     break;
   }

   return 0;   
}

2 个答案:

答案 0 :(得分:2)

您已将line初始化为:

char *line = NULL; 

但你没有为它分配任何记忆。

添加

line = malloc(max_length+1);
在致电read_line之前

答案 1 :(得分:1)

您拥有char* line = NULL,然后为new动态分配内存。

但是,您在函数line中传递了read_line as参数,然后尝试使用i-th运算符访问其[]元素,从而获得了SEG。故障。

为了解决这个问题,你可以这样做:

char *line = NULL;
int max_length = 100;
line = malloc( (max_length + 1) * sizeof(char) ); // +1 for the null terminator

此外,在stderr中打印菜单并不常见。 stderr是错误流。菜单不是错误。

我会写,而不是:

fprintf ( stderr, "\nOptions:\n" );

fprintf ( stdout, "\nOptions:\n" ); // or use printf

您可能还想阅读:Why not casting the result of malloc in C?

另一个更重要的logical error,接近导致您的细分错误的错误。

您没有初始化文件指针pf,但是您希望在read_line中读取它。

FILE *pf = NULL;                  // the file pointer
char *line = NULL;                // fix the initial logical error
int max_length = 100;
line = malloc(max_length + 1);
int year;
struct Book *new = NULL;
new = (struct Book *) malloc ( sizeof(struct Book) );
fprintf(stderr,"\nEnter Title:");
read_line ( pf, line, max_length ); // Here, you pass pf uninitialized

然后在read_line(),您可以:

char ch;       // you may want to change that in: int char;
ch = fgetc(pf);

那么,文件指针设置在哪里?无处!

而且,你将你的变量命名为pf,但它是一个文件指针,所以fp会更好,恕我直言。

也许还有其他错误,但我认为现在已经足够了。