【问题标题】:a list like structure in cc中的类似结构的列表
【发布时间】:2016-06-11 02:30:02
【问题描述】:

我希望实现一个简单的数据结构,比如链表,但我不需要任何复杂的东西,或者我需要吗?

假设我有一个图书数据结构。

typedef struct {
    char* name;
    size_t number;
} book;

我想将这些附加到越来越多的书籍列表中,这些书籍在内存中应该看起来像这样。

------------------------
| 0 | 1 | 2 | 3 | ......
------------------------

因为我除了添加一堆书并且从不改变顺序并且只在程序结束时释放书籍之外没有做任何花哨的事情,所以我不需要链表的所有功能。

我试图用这样的双指针来创建书籍的原始结构。

public book** add_bookd(book** books, book* b) {
    static int index = -1;
    int count;
    index++;
    count = index + 1;
    book** books = realloc(books, sizeof(book*)*count);
    books[index] = calloc(1, sizeof(book));
    copy_bookd(books[index], b); //this is just a series of memcopy
                                 //since i have POD data types
   return books;
}

我用一个非常简单的字符尝试过,但我遇到了段错误。

char** double_pointer(char** a, char* b) {
    static int index = -1; int count;
    index++;
    count = index + 1;
    a = realloc(a, sizeof(char*)*count);
    for(int i = 0; i < index; i++) {
        a[i] = calloc(1, sizeof(char));
        a[i] = b;
        printf("%s\n",a[i]);
    }
    return a;
}


char** tmp = calloc(1, sizeof(char*));
for(int i = 0; i < 3; i++) {
    double_pointer(tmp, "a");
    //printf("%s\n",tmp)); //seg faults here
}

这是用链表更好地实现的东西吗?为什么我会出现段错误?我的意思是 gdb 显示我在那条线上崩溃了。

当我打印 tmp gdb 报告的值时

p tmp $1 = (char **) 0x0

尝试访问 tmp[0] gdb 表示无法访问 0x0 处的内存。

【问题讨论】:

  • public 在 C 语言中?
  • static int index = -1; :这是个坏主意。因为它只能作为一个持续的过程。换句话说,你将无法处理另一个链表。
  • tmpchar **(指向指针的指针),而不是空终止字符串的开头。
  • 您似乎有某种误解,但很难说出那是什么,因为您没有提供一个完整的程序来证明您的问题。如果您提供MCVE,我们可以为您提供更好的帮助。
  • 在调用任何内存分配函数(malloc, calloc, realloc()时总是检查(!=NULL)返回值以保证操作成功。调用realloc()时不要直接赋值返回值到目标点。因为如果 realloc() 失败(它可以做到),那么分配内存的原始点(在这种情况下为 a )将被覆盖/丢失。这会导致内存泄漏以及对 @987654335 的任何后续访问@ 是未定义的行为,可能导致段错误事件。相反,使用临时指针并在分配给目标指针之前检查临时指针不为 NULL

标签: c pointers memory-management


【解决方案1】:

这些行:

book** books = realloc(books, sizeof(book*)*count);
books[index] = calloc(1, sizeof(book));
copy_bookd(books[index], b);

出现了一些问题,尤其是缺少错误检查。

建议:

book* temp = realloc(*books, sizeof(book*)*count);

if( temp != NULL )
{ // then, realloc successful

    *books = temp;
     (*books)[index] = calloc(1, sizeof(book));
     if( (*books)[index] != NULL )
     { // then, calloc successful
         copy_bookd((*books)[index], b);
     }

     else
     {
         // handle calloc failure
     }
}

else
{
    // handle realloc failure
}

【讨论】:

    【解决方案2】:

    所以您正在寻找一种存储大量书籍的可能性,您可能只会添加这些书籍?这确实可以通过与您已有的方法非常相似的方法来完成。但是让我们从下往上开始:

    基本上,您在内存中的某个地方存储了一个“数组”书籍,即在一个连续的内存位置中的几个struct book。您可以使用指向第一本书的指针指向该数组:

    struct book * books;
    

    不知何故,您还需要能够知道该数组的结束位置。因此,我看到的两种可能性是使用标记值(类似于 C 字符串对 '\0' 所做的事情)或仅跟踪数组的大小。我会选择后者:

    size_t count;
    

    由于您不想迷路,我们使用结构化编程技术并将这两条信息绑定在一起:

    struct bookstore {
      struct book * books;
      size_t count;
    };
    

    为了与这样的商店合作,应该把常见的操作放在花哨的名字后面。这称为抽象,可能是编程时最重要的事情。在 C 中执行此操作的设备是函数。从一个开始初始化一个空存储:

    char init_store(struct bookstore * store) {
      assert(store != NULL);
      store->count = 0;
      store->books = malloc(0);
      return (store->books == NULL) ? 0 : 1;
    }
    

    并添加它的双重功能来“完成”商店:

    void finish_store(struct bookstore * store) {
      assert(store != NULL);
      assert(store->books != NULL);
      store->count = 0;
      free(store->books);
      store->books = NULL;
    }
    

    这些函数仅在已经存在的struct bookstore 实例上运行。您可以在堆栈或堆上分配它们(通过malloc,不要忘记free)。

    现在到关键部分:添加一本书。为此,您需要扩大“数组”以便能够在其中存储新项目。这是使用realloc 完成的。然后,您只需将书复制到空闲位置即可添加:

    char add_book(struct bookstore * store, struct book book) {
      assert(store != NULL);
      assert(store->books != NULL);
      struct book * books =
        realloc(store->books, (store->count + 1) * sizeof(struct book));
      if (books == NULL) {
        return 0;
      }
      books[store->count] = book;
      store->books = books;
      store->count += 1;
      return 1;
    }
    

    这就是我的处理方式,尽管还有很多优化空间。请注意,上述代码未经测试。

    该方法与您的方法基本相同:您将使用指向(包含)书籍指针的指针。重要的区别是大小也会被存储。您的代码为此使用了一个静态局部变量,这基本上将您限制为只有一个书籍“列表”。作为建议:避免像地狱一样的全局或静态状态。

    【讨论】:

      【解决方案3】:

      有很多方法可以跟踪多个结构。关于最简单的动态结构是结构的动态数组。相同的内存管理方法将适用,但您可以添加另一个外部结构来帮助保持与该书籍集合相关的信息。例如。如果您有多个感兴趣的集合,您可以执行类似的操作:

      typedef {
          size_t nbooks;
          size_t allocsz;
          char colname[16];
          struct {
              char* name;
              size_t number;
          } book;
      } collection;
      

      这样的方法允许使用一个结构来保存它包含的书籍数量、当前分配大小和集合的唯一名称(例如 fictionnon-fiction、等...)

      您可以考虑这样的事情是否会添加足够的额外数据来满足您的需求。至于结构数组的基本处理,只需要动态声明的数组即可。然后由您来跟踪当前分配的数量、填充的数量,然后在填充的数量达到您的限制时发送到realloc

      要将一本书添加到您的收藏中,您可以执行一些简单的操作,例如:

      void addbook (book **books, char *name, size_t num, size_t *n, size_t *max)
      {
          (*books)[*n].name = strdup (name);
          (*books)[*n].number = num;
      
          if ( ++(*n) >= *max) {  /* realloc */
              void *tmp = realloc (*books, sizeof **books * 2 * *max);
              if (!tmp) {
                  fprintf (stderr, "error: realloc, virtual memory exhausted.\n");
                  exit (EXIT_FAILURE);
              }
              *books = tmp;
              memset (*books + *max, 0, sizeof **books * *max);
              *max *= 2;
          }
      }
      

      传递结构数组的地址,以及要添加的书名编号,即当前书数,以及当前的最大值。 strdup 用于为名称分配空间,分配书号,然后进行重新分配检查。仅此而已。

      数组方法的一个简短示例是:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      enum { MAXB = 64 };
      
      typedef struct {
          char* name;
          size_t number;
      } book;
      
      void addbook (book **books, char *name, size_t num, size_t *n, size_t *max);
      
      int main (void) {
      
          book *books = calloc (MAXB, sizeof *books); /* check for error cond */
          char name[MAXB] = "";
          size_t maxb = MAXB, i, idx = 0, len = 0, n = 0, nadd = 0;
      
          printf ("\n number of books to enter: ");
          while (scanf ("%zu%*c", &nadd) != 1) {
              printf ("  invalid number, try again: ");
          }
      
          for (i = 0; i < nadd; i++) {  /* take input and add to books */
              size_t nlen;
              printf (" enter book title : ");
              while (scanf ("%[^\n]%*c", name) != 1) {
                  printf ("  invalid name, try again: ");
              }
              printf (" enter book number: ");
              while (scanf ("%zu%*c", &n) != 1) {
                  printf ("  invalid number, try again: ");
              }
              addbook (&books, name, n, &idx, &maxb);
      
              nlen = strlen (name);  /* max length for output formatting */
              if (nlen > len) len = nlen;
          }
          putchar ('\n');
      
          for (i = 0; i < idx; i++) {  /* print the books added */
              printf (" book[%2zu]  %-*s   %zu\n", i, (int)len,
                      books[i].name, books[i].number);
          }
      
          for (i = 0; i < idx; i++)   /* free allocated memory */
              free (books[i].name);
          free (books);
      
          return 0;
      }
      
      void addbook (book **books, char *name, size_t num, size_t *n, size_t *max)
      {
          (*books)[*n].name = strdup (name);
          (*books)[*n].number = num;
      
          if ( ++(*n) >= *max) {  /* realloc */
              void *tmp = realloc (*books, sizeof **books * 2 * *max);
              if (!tmp) {
                  fprintf (stderr, "error: realloc, virtual memory exhausted.\n");
                  exit (EXIT_FAILURE);
              }
              *books = tmp;
              memset (*books + *max, 0, sizeof **books * *max);
              *max *= 2;
          }
      }
      

      使用/输出示例

      $ ./bin/struct_books_single
      
       number of books to enter: 4
       enter book title : Moby Dick
       enter book number: 23478
       enter book title : Tom Sawyer
       enter book number: 23479
       enter book title : Of mice and men
       enter book number: 23480
       enter book title : Of other flesh & tongue
       enter book number: 23510
      
       book[ 0]  Moby Dick                 23478
       book[ 1]  Tom Sawyer                23479
       book[ 2]  Of mice and men           23480
       book[ 3]  Of other flesh & tongue   23510
      

      查看示例,如果您有任何问题,请告诉我。您可以添加尽可能多的书籍,只要您的记忆可以容纳。所有书籍在退出时都会被释放。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-03
        • 1970-01-01
        • 1970-01-01
        • 2015-08-31
        • 1970-01-01
        • 1970-01-01
        • 2022-06-15
        • 1970-01-01
        相关资源
        最近更新 更多