C – 结构体数组函数指针

huangapple go评论98阅读模式
英文:

C - Array of structs function pointer

问题

以下是翻译好的部分:

第一个程序在成功使用指针调用函数。我现在正在尝试使用结构体数组使相同的机制工作。最后一行(已注释)不起作用,我看不出任何区别。我做错了什么?

在第二个程序中,这一行的格式可能有问题吗?
void (*func_ptr)(struct book, int) = printBook;

我使用了这个格式:<返回类型>(<*变量名>)(<参数类型>,<参数类型>,...)=函数

这个可以正常工作...

// C使用指针调用函数
#include <stdio.h>
#include <string.h>

void func(int a) {   // 正常函数
  printf("a的值为%d\n", a);
}

int main() {
  void (*func_ptr)(int) = func;         // 将指针分配给函数地址
  func_ptr(10); // 调用func
  return 0;
}

这个不工作...

// 看起来相同但不工作
#include <stdio.h>
#include <string.h>

// 数据结构
struct book {                          // 结构体数据
  int     book_id;                     // 书籍ID
  int     (*prtFunc) ( struct book  ); // 最终是打印函数的地址
  char    title[50];                   // 书名
  char    author[50];                  // 作者
};

struct book arBookList[10];

void printBook ( struct book arBookList[], int id ) {           // 测试结构体访问
  printf ( "book_id       : %d\n", arBookList[id].book_id );    
  printf ( "func地址     : %p\n", arBookList[id].prtFunc );    // 最终这将调用函数
  printf ( "title         : %s\n", arBookList[id].title   );    // 字符串数据测试
  printf ( "author        : %s\n", arBookList[id].author  );    // 字符串
  printf ( "\n" );
}

int main (  ) {

  arBookList[0].book_id =  0 ;
  arBookList[0].prtFunc = printBook;
  strcpy ( arBookList[0].title, "This Little Piggy" );
  strcpy ( arBookList[0].author, "Bad Wolf" );
  printBook (arBookList, 0 );                             // 使用函数调用显示数据

  arBookList[1].book_id =  1 ;
  arBookList[1].prtFunc = printBook;
  strcpy ( arBookList[1].title, "Mary Had a Lamb" );
  strcpy ( arBookList[1].author, "Snow Fleece" );
  printBook (arBookList, 1 );                             // 使用函数调用显示数据

  // 使用指针调用函数
  void (*func_ptr)(struct book, int) = printBook;         // 将指针分配给函数地址
 
  func_ptr (arBookList, 1 );                              // <--- 不起作用,为什么?
  // 注释掉上面的行可以正常编译
  // 但我觉得除了不同的调用参数外,看起来都没问题

  return 0;
}

供以后参考: 使用'O'的修正清单下面,我达到了我的最终目标,即能够调用保存在结构体中的函数指针来调用函数(在这种情况下是printBook),就像这样:arBookList[1].prtFunc (arBookList, 1 );

英文:

The first program below calls a function using a pointer successfully. I am now trying to get the same mechanism to work using an array of structs. The last line (commented out) will not work, and I do not see any differences. What am I doing wrong?

Is it possible I have the wrong format for this line in the second program?
> *void (func_ptr)(struct book, int) = printBook;<br>
I used this format: &lt;ret type&gt; (&lt;*varname&gt;) (&lt;parm type&gt;, &lt;parm type&gt;,...) = function

This works...

// C call function using pointer
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

void func(int a)  {   // normal function
  printf(&quot;Value of a is %d\n&quot;, a);
}

int main() {
  void (*func_ptr)(int) = func;         // assign ptr to func address
  func_ptr(10); // call func
  return 0;
}

This does not work...

// Looks the same but does not work
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

// data structure
struct book {                          // struct data
  int     book_id;                     // book id
  int     (*prtFunc) ( struct book  ); // eventually, addr of print function
  char    title[50];                   // book title
  char    author[50];                  // book author
};

struct book arBookList[10];

void printBook ( struct book arBookList[], int id ) {           // test struct access
  printf ( &quot;book_id       : %d\n&quot;, arBookList[id].book_id );    
  printf ( &quot;func addr     : %p\n&quot;, arBookList[id].prtFunc );    // eventually this will call function
  printf ( &quot;title         : %s\n&quot;, arBookList[id].title   );    // string data test
  printf ( &quot;author        : %s\n&quot;, arBookList[id].author  );    // string
  printf ( &quot;\n&quot; );
}

int main (  ) {

  arBookList[0].book_id =  0 ;
  arBookList[0].prtFunc = printBook;
  strcpy ( arBookList[0].title, &quot;This Little Piggy&quot; );
  strcpy ( arBookList[0].author, &quot;Bad Wolf&quot; );
  printBook (arBookList, 0 );                             // show data using function call

  arBookList[1].book_id =  1 ;
  arBookList[1].prtFunc = printBook;
  strcpy ( arBookList[1].title, &quot;Mary Had a Lamb&quot; );
  strcpy ( arBookList[1].author, &quot;Snow Fleece&quot; );
  printBook (arBookList, 1 );                             // show data using function call

  // call function using pointer
  void (*func_ptr)(struct book, int) = printBook;         // assign ptr to func address
 
  func_ptr (arBookList, 1 );                              // &lt;--- does not work, why ???
  // commenting out the above line compiles ok 
  // but it looks ok to me except for different call parameters

  return 0;
}

For future reference: With 'O's corrected listing below I reached my final goal which was to be able to call a saved function pointer in struct to call a function (in this case printBook) like this: arBookList[1].prtFunc (arBookList, 1 );

答案1

得分: 4

Here's the translated code portion:

对于函数指针传递了错误的参数。您传递了指针,但它期望的是结构体。

修正后的程序:

```c
struct book {                          // 结构体数据
  int     book_id;                     // 书籍ID
  void     (*prtFunc) (struct book  *, int); // 最终,打印函数的地址
  char    title[50];                   // 书籍标题
  char    author[50];                  // 书籍作者
};

struct book arBookList[10];

void printBook (struct book *arBookList, int id) {           // 测试结构体访问
  printf("book_id       : %d\n", arBookList[id].book_id);
  printf("func addr     : %p\n", arBookList[id].prtFunc);    // 最终这将调用函数
  printf("title         : %s\n", arBookList[id].title);       // 字符串数据测试
  printf("author        : %s\n", arBookList[id].author);      // 字符串
  printf("\n");
}

int main() {

  arBookList[0].book_id = 0;
  arBookList[0].prtFunc = printBook;
  strcpy(arBookList[0].title, "This Little Piggy");
  strcpy(arBookList[0].author, "Bad Wolf");
  printBook(arBookList, 0);                             // 使用函数调用显示数据

  arBookList[1].book_id = 1;
  arBookList[1].prtFunc = printBook;
  strcpy(arBookList[1].title, "Mary Had a Lamb");
  strcpy(arBookList[1].author, "Snow Fleece");
  printBook(arBookList, 1);                             // 使用函数调用显示数据

  // 使用指针调用函数
  void (*func_ptr)(struct book *, int) = printBook;         // 将指针分配给函数地址

  func_ptr(arBookList, 1);                              // <--- 不起作用,为什么???
  // 注释掉上面的行,编译没有问题
  // 但在我看来,除了调用参数不同以外,一切看起来都还好
  return 0;
}

此外,您的结构体中的函数指针类型也有问题。

英文:

For function pointer takes wrong parameter. You pass pointer and it is expecting the struct.

The corrected program:

struct book {                          // struct data
  int     book_id;                     // book id
  void     (*prtFunc) (struct book  *, int); // eventually, addr of print function
  char    title[50];                   // book title
  char    author[50];                  // book author
};

struct book arBookList[10];

void printBook ( struct book *arBookList, int id ) {           // test struct access
  printf ( &quot;book_id       : %d\n&quot;, arBookList[id].book_id );    
  printf ( &quot;func addr     : %p\n&quot;, arBookList[id].prtFunc );    // eventually this will call function
  printf ( &quot;title         : %s\n&quot;, arBookList[id].title   );    // string data test
  printf ( &quot;author        : %s\n&quot;, arBookList[id].author  );    // string
  printf ( &quot;\n&quot; );
}

int main (  ) {

  arBookList[0].book_id =  0 ;
  arBookList[0].prtFunc = printBook;
  strcpy ( arBookList[0].title, &quot;This Little Piggy&quot; );
  strcpy ( arBookList[0].author, &quot;Bad Wolf&quot; );
  printBook (arBookList, 0 );                             // show data using function call

  arBookList[1].book_id =  1 ;
  arBookList[1].prtFunc = printBook;
  strcpy ( arBookList[1].title, &quot;Mary Had a Lamb&quot; );
  strcpy ( arBookList[1].author, &quot;Snow Fleece&quot; );
  printBook (arBookList, 1 );                             // show data using function call

  // call function using pointer
  void (*func_ptr)(struct book *, int) = printBook;         // assign ptr to func address
 
  func_ptr (arBookList, 1 );                              // &lt;--- does not work, why ???
  // commenting out the above line compiles ok 
  // but it looks ok to me except for different call parameters

  return 0;
}

Also your function pointer in the struct had a wrong type.

https://godbolt.org/z/5E1E8v7j3

答案2

得分: 1

In struct book you have prtFunc declared as

    int (*prtFunc)(struct book);  // eventually, addr of print function

And in main you have error on

    arBookList[0].prtFunc = printBook;

But printBook is

void printBook(struct book arBookList[], int id)

So the actual parameters do not match the declaration.

another issue

A bit off-topic but if the function is the same for all books its address should not be replicated in every book in the array.

Example: using a collection instead of an array

typedef struct
{                     // struct data
    int  book_id;     // book id
    char title[50];   // book title
    char author[50];  // book author
} Book; // a single book

typedef struct
{
    size_t limit;  // capacity
    size_t size;   // actual size
    Book   book_list[10];
    int (*print_book)(Book*);
} Books; // a collection

This way we have a collection Books that has a size and limit. And also a pointer to a function that prints a single book. And the books are inside the struct so you can use it as a whole.

Your test collection can be declared in a modern way as

    Books coll = {
        .limit = 10,
        .size  = 2,
        .book[0] =
            {.id     = 42,
             .title  = "This Little Piggy",
             .author = "Bad Wolf"},
        .book[1] =
            {.id     = 4242,
             .title  = "Mary Had a Lamb",
             .author = "Snow Fleece"},
        .print_book =
            printBook  // this function prints a book
    };

This is easier to read and test.

a version of printBook

void printBook(Book* book)
{
    printf(
        "\
    book_id       : %d\n\
    title         : %s\n\
    author        : %s\n\
\n",
        book->id, book->title, book->author);
};

A single printf (or puts) is way faster and easier to read and type.

printing all books

It can be convenient to have a function to print the complete Books struct:

int print_all(Books* col)
{
    if (col == NULL) return -1;
    printf(
        "    there are %llu of %llu books\n\n", col->size,
        col->limit);
    for (size_t i = 0; i < col->size; i += 1)
        col->print_book(&col->book[i]);
    printf("\n");
    return 0;
};

Since it uses the embedded pointer in the Books struct, it is easy to change style just by changing the pointer inside the struct.

using the 2 functions

    coll.print_book(&coll.book[0]);
    printBook(&coll.book[1]);
   // print all
    printf("test: print the collection\n\n");
    print_all(&coll);

Here we call printBook to print each book, by name and by reference using the pointer inside the collection. Then the collection is printed.

A complete example

// Looks the same but does not work
#include <stdio.h>
#include <string.h>

typedef struct
{                     // struct data
    int  id;          // book id
    char title[50];   // book title
    char author[50];  // book author
} Book;               // a single book

typedef struct
{
    size_t limit;  // capacity
    size_t size;   // actual size
    Book   book[10];
    void (*print_book)(Book*);
} Books;                          // a collection

void print_book(Book* book);      // single book
void print_book_alt(Book* book);  // single book, one line
int  print_all(Books*);           // collection

int main(void)
{
    Books coll = {
        .limit = 10,
        .size  = 2,
        .book[0] =
            {.id     = 42,
             .title  = "This Little Piggy",
             .author = "Bad Wolf"},
        .book[1] =
            {.id     = 4242,
             .title  = "Mary Had a Lamb",
             .author = "Snow Fleece"},
        .print_book =
            print_book  // this function prints a book
    };

    coll.print_book(&coll.book[0]);
    print_book(&coll.book[1]);
    // print all
    printf("test: print the collection\n\n");
    print_all(&coll);
    // test alternate print function
    printf("test: alternate print\n\n");
    print_book_alt(&coll.book[0]);
    print_book_alt(&coll.book[1]);
    // adds a new book
    Book other = {
        .id     = 17,
        .title  = "My time with Ms. Lane",
        .author = "Super Man"};
    printf("\ntest: new book\n\n");
    print_book_alt(&other);
    // updates collection
    coll.book[2] = other;
    coll.size += 1;
    print_all(&coll);
    // changes printing
    coll.print_book = print_book_alt;
    printf("\ntest: print the collection again\n\n");
    print_all(&coll);
    return 0;
}

void print_book(Book* book)
{
    printf(
        "\
    book_id       : %d\n\
    title         : %s\n\
    author        : %s\n\
\n",
        book->id, book->title, book->author);
};

int print_all(Books* col)
{
    if (col == NULL) return -1;
    printf(
        "    there are %llu of %llu books\n\n", col->size,
        col->limit);
    for (size_t i = 0; i < col->size; i += 1)
        col->print_book(&col->book[i]);
    printf("\n");
    return 0;
};

void print_book_alt(Book* book)
{
    printf(
        "%8d, \"%s\", [%s]\n", book->id, book->title,
        book->author);
}

the output of the example

    book_id       : 42
title         : This Little Piggy
author        : Bad Wolf
book_id       : 4242
title         : Mary Had a Lamb
author        : Snow Fleece
test: print the collection
there are 2 of 10 books
book_id       : 42
title         : This Little Piggy
author        : Bad Wolf
book_id       : 4242
title         : Mary Had
<details>
<summary>英文:</summary>
In `struct book` you have `prtFunc` declared as 
```C
int (*prtFunc)(struct book);  // eventually, addr of print function

And in main you have error on

    arBookList[0].prtFunc = printBook;

But printBook is

void printBook(struct book arBookList[], int id)

So the acual parameters does not match the declaration.

another issue

A bit off-topic but if the function is the same for all books its address should not be replicated in every book in the array.

Example: using a collection instead of an array

typedef struct
{                     // struct data
    int  book_id;     // book id
    char title[50];   // book title
    char author[50];  // book author
} Book; // a single book

typedef struct
{
    size_t limit;  // capacity
    size_t size;   // actual size
    Book   book_list[10];
    int (*print_book)(Book*);
} Books; // a collection

This way we have a collection Books that has a size and limit. And also a pointer to a function that prints a single book. And the books are inside the struct so you can use it as a whole.

Your test collection can be declared in a modern way as

    Books coll = {
        .limit = 10,
        .size  = 2,
        .book[0] =
            {.id     = 42,
             .title  = &quot;This Little Piggy&quot;,
             .author = &quot;Bad Wolf&quot;},
        .book[1] =
            {.id     = 4242,
             .title  = &quot;Mary Had a Lamb&quot;,
             .author = &quot;Snow Fleece&quot;},
        .print_book =
            printBook  // this function prints a book
    };

This is easier to read and test.

a version of print_book

void print_book(Book* book)
{
    printf(
        &quot;\
    book_id       : %d\n\
    title         : %s\n\
    author        : %s\n\
\n&quot;,
        book-&gt;id, book-&gt;title, book-&gt;author);
};

A single printf (or puts) is way more faster and easier to read and type

printing all books

It can be convenient to have a function to print the complete Books struct:

int print_all(Books* col)
{
    if (col == NULL) return -1;
    printf(
        &quot;    there are %llu of %llu books\n\n&quot;, col-&gt;size,
        col-&gt;limit);
    for (size_t i = 0; i &lt; col-&gt;size; i += 1)
        col-&gt;print_book(&amp;col-&gt;book[i]);
    printf(&quot;\n&quot;);
    return 0;
};

Since it uses the embedded pointer in the Books struct it is easy to change style just by changing the pointer inside the struct

using the 2 functions

    coll.print_book(&amp;coll.book[0]);
    print_book(&amp;coll.book[1]);
   // print all
    printf(&quot;test: print the collection\n\n&quot;);
    print_all(&amp;coll);

here we call print_book to print each book, by name and by reference using the pointer inside collection. Then the collection is printed.

A complete example

// Looks the same but does not work
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

typedef struct
{                     // struct data
    int  id;          // book id
    char title[50];   // book title
    char author[50];  // book author
} Book;               // a single book

typedef struct
{
    size_t limit;  // capacity
    size_t size;   // actual size
    Book   book[10];
    void (*print_book)(Book*);
} Books;                          // a collection

void print_book(Book* book);      // single book
void print_book_alt(Book* book);  // single book,one line
int  print_all(Books*);           // collection

int main(void)
{
    Books coll = {
        .limit = 10,
        .size  = 2,
        .book[0] =
            {.id     = 42,
             .title  = &quot;This Little Piggy&quot;,
             .author = &quot;Bad Wolf&quot;},
        .book[1] =
            {.id     = 4242,
             .title  = &quot;Mary Had a Lamb&quot;,
             .author = &quot;Snow Fleece&quot;},
        .print_book =
            print_book  // this function prints a book
    };

    coll.print_book(&amp;coll.book[0]);
    print_book(&amp;coll.book[1]);
    // print all
    printf(&quot;test: print the collection\n\n&quot;);
    print_all(&amp;coll);
    // test alternate print f.
    printf(&quot;test: alternate print\n\n&quot;);
    print_book_alt(&amp;coll.book[0]);
    print_book_alt(&amp;coll.book[1]);
    // adds a new book
    Book other = {
        .id     = 17,
        .title  = &quot;My time with Ms. Lane&quot;,
        .author = &quot;Super Man&quot;};
    printf(&quot;\ntest: new book\n\n&quot;);
    print_book_alt(&amp;other);
    // updates collection
    coll.book[2] = other;
    coll.size += 1;
    print_all(&amp;coll);
    // changes printing
    coll.print_book = print_book_alt;
    printf(&quot;\ntest: print the collection again\n\n&quot;);
    print_all(&amp;coll);
    return 0;
}

void print_book(Book* book)
{
    printf(
        &quot;\
    book_id       : %d\n\
    title         : %s\n\
    author        : %s\n\
\n&quot;,
        book-&gt;id, book-&gt;title, book-&gt;author);
};

int print_all(Books* col)
{
    if (col == NULL) return -1;
    printf(
        &quot;    there are %llu of %llu books\n\n&quot;, col-&gt;size,
        col-&gt;limit);
    for (size_t i = 0; i &lt; col-&gt;size; i += 1)
        col-&gt;print_book(&amp;col-&gt;book[i]);
    printf(&quot;\n&quot;);
    return 0;
};

void print_book_alt(Book* book)
{
    printf(
        &quot;%8d, \&quot;%s\&quot;, [%s]\n&quot;, book-&gt;id, book-&gt;title,
        book-&gt;author);
}

the output of the example

    book_id       : 42
title         : This Little Piggy
author        : Bad Wolf
book_id       : 4242
title         : Mary Had a Lamb
author        : Snow Fleece
test: print the collection
there are 2 of 10 books
book_id       : 42
title         : This Little Piggy
author        : Bad Wolf
book_id       : 4242
title         : Mary Had a Lamb
author        : Snow Fleece
test: alternate print
42, &quot;This Little Piggy&quot;, [Bad Wolf]
4242, &quot;Mary Had a Lamb&quot;, [Snow Fleece]
test: new book
17, &quot;My time with Ms. Lane&quot;, [Super Man]
there are 3 of 10 books
book_id       : 42
title         : This Little Piggy
author        : Bad Wolf
book_id       : 4242
title         : Mary Had a Lamb
author        : Snow Fleece
book_id       : 17
title         : My time with Ms. Lane
author        : Super Man
test: print the collection again
there are 3 of 10 books
42, &quot;This Little Piggy&quot;, [Bad Wolf]
4242, &quot;Mary Had a Lamb&quot;, [Snow Fleece]
17, &quot;My time with Ms. Lane&quot;, [Super Man]

In this example

  • the printing function is called twice, one by name and other using the pointer in Books
  • a book is added to the collection
  • an alternate printing function is added
  • the function pointer inside Books is changed
  • the collection is again printed

C with classes: turning the Container generic using function pointers

Here is the struct for the collection in the previous example:

typedef struct
{
    size_t limit;  // capacity
    size_t size;   // actual size
    Book   book_list[10];
    int (*print_book)(Book*);
} Books; // a collection

It solves many things, since it can be treated as a whole and encapsulates the current and total size limits.

But:

  • if Book allocates memory there is no way to free it.
  • The collecion's items are themselves inside the struct
    • resize is difficult
    • delete an item is difficult
  • It is the same file so a change in the container OR in the items triggers a change in the same code

>It would be better to have the code for the container independent from the item's code.

Example: a more generic container

Sure it is a list of items. But could be a linked list, a set, a queue, an array... C++ calls it a container. java and others call them collection.

Consider

typedef struct
{
    size_t limit;             // capacity
    size_t size;              // actual size
    void** item;              // an array of pointers to items
    void* (*copy)(void*);     // copy
    void* (*destroy)(void*);  // destructor
    int (*show)(void*);       // print
} Container;                  // a collection

Now we have an array of pointers to items and:

  • it is easy to resize: we have just references to the item
  • we have no reference to the actual definition of the item
  • we know how to copy an item in order to insert it into the container
  • we know how to destroy an item when destroying the container or managing the container's contents.
  • we know how to show one item at the screen

But the container have no idea about its actual contents: it is the user of the container that produces the code, and the container's just saves the function pointers.

There is no need to even recompile the code for container.c

&gt; now we can implement the container code with no mention to the items: a container can contain anything: books, SDL screens, SDL renderers, invoices, movie tickets...

A minimum set of functions:

Container* ctn_create(
  size_t, void* (*copy)(void*), void* (*destroy)(void*),
  int (*show)(void*));
Container* ctn_destroy(Container*);
int        ctn_insert(void*, Container*);
int        ctn_show(Container*);

This is the mininum to write an example, like this one down here.

Book as an Item in item.h

#pragma once
#include &lt;stdio.h&gt;

typedef struct
{                   // struct data
    size_t id;      // book id
    char*  title;   // book title
    char*  author;  // book author
    size_t n_pages;   
} Book;             // a single book

int   print_book(void*);
int   print_book_alt(void*);  // one liner
void* copy_book(void*);
void* create_book(void);
void* delete_book(void*);

A change in container code does not lead to a change in the item code, thanks to the function pointers. In C++ they would be called copy constructor, destructor and operator << overload of Item.

Implementation: Book

item.h

#pragma once
#include &lt;stdio.h&gt;

typedef struct
{                   // struct data
    size_t id;      // book id
    char*  title;   // book title
    char*  author;  // book author
    size_t n_pages;   
} Book;             // a single book

int   print_book(void*);
int   print_book_alt(void*);  // one liner
void* copy_book(void*);
void* create_book(void);
void* delete_book(void*);

The functions now accept and return void, but there is a definition for Book so all of them can cast the pointers as needed. This is the same approach used in qsort from stdlib for example, since ever. Like callbacks in java or javascript.

Note that now Book uses pointers to title and author and has a new field for # of pages. So memory is allocated to the exact size needed, instead of the fixed arrays of the first example.

Problem is that now to destroy a container the code has to know how to free these fields and the Book itself. Here is the use )and need) for a callback, a function pointer.

item.c a simple implementation

#include &quot;item.h&quot;

#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

void* copy_book(void* book)
{
    if (book == NULL) return NULL;
    Book* one     = (Book*)book;
    Book* other   = (void*)malloc(sizeof(Book));
    other-&gt;id     = one-&gt;id;
    other-&gt;author = malloc(1 + sizeof(one-&gt;author));
    strcpy(other-&gt;author, one-&gt;author);
    other-&gt;title = malloc(1 + sizeof(one-&gt;title));
    strcpy(other-&gt;title, one-&gt;title);
    other-&gt;n_pages = one-&gt;n_pages;
    return (void*)other;
};

void* create_book(void)
{
    static size_t id         = 1000;
    char          author[40] = {0};
    char          title[40]  = {0};
    Book*         one        = (void*)malloc(sizeof(Book));

    sprintf(&amp;author[0], &quot;Author %llu&quot;, id);
    one-&gt;author = malloc(1 + strlen(author));
    strcpy(one-&gt;author, author);

    sprintf(&amp;title[0], &quot;Title %llu&quot;, id);
    one-&gt;title = malloc(1 + strlen(title));
    strcpy(one-&gt;title, title);

    one-&gt;id      = id++;
    one-&gt;n_pages = 200 + rand() % 155;
    return (void*) one;
};

void* delete_book(void* book)
{
    if (book == NULL) return NULL;
    Book* one = (Book*)book;
    free(one-&gt;author);
    free(one-&gt;title);
    free(one);  // simple, void does not allocate
    return NULL;
}

int print_book(void* book)
{
    if (book == NULL) return 0;
    Book* one = (Book*)book;
    printf(
        &quot;\
    Book ID       : %llu\n\
    title         : %s [%llu pages]\n\
    author        : %s\n\
\n&quot;,
        one-&gt;id, one-&gt;title, one-&gt;n_pages, one-&gt;author);
    return 0;
};

int print_book_alt(void* book)
{
    if (book == NULL) return 0;
    Book* one = (Book*)book;
    printf(
        &quot;%8llu, \&quot;%s [%llu pages]\&quot;, [%s]\n&quot;, one-&gt;id,
        one-&gt;title, one-&gt;n_pages, one-&gt;author);
    return 0;
}

No reference to Container: just includes item.h to get Book and function prototypes.

Note the addition of create_book(), a handy factory function that generates a new book for each time it is called. This way is easy to test with any number of items with no input files.

Code for a generic Container

Note that the container does no even include item.h

container.h

#pragma once
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

typedef struct
{
    size_t limit;             // capacity
    size_t size;              // actual size
    void** item;              // an array of pointers to items
    void* (*copy)(void*);     // copy
    void* (*destroy)(void*);  // destructor
    int (*show)(void*);       // print
} Container;                  // a collection

Container* ctn_create(
    size_t, void* (*copy)(void*), void* (*destroy)(void*),
    int (*show)(void*));
Container* ctn_destroy(Container*);
int        ctn_insert(void*, Container*);
int        ctn_show(Container*);

And the Container is now an array of pointers to items. Any item, since the user when calling ctn_create() passes the addresses of the functions.

This way a program can easily manage an array of items, even of diferent ones.

container.c

#pragma once
#include &quot;container.h&quot;

Container* ctn_create(
    size_t size, void* (*copy)(void*),
    void* (*destroy)(void*), int (*show)(void*))
{
    Container* ctn =
        (Container*)malloc(sizeof(Container) * size);
    if (ctn == NULL) return NULL;
    ctn-&gt;limit = size;  // limit
    ctn-&gt;size  = 0;     // now empty, of course
    ctn-&gt;item = (void**)malloc(size * sizeof(void*));
    if (ctn-&gt;item == NULL)
    {  // could not allocate
        free(ctn);
        return NULL;
    }
    ctn-&gt;copy    = copy;
    ctn-&gt;destroy = destroy;
    ctn-&gt;show    = show;
    return ctn;
}

Container* ctn_destroy(Container* ctn)
{  // to destroy a container we need to know how to
    // delete the voids: they can allocate memory
    if (ctn == NULL) return NULL;
    for (size_t ix = 0; ix &lt; ctn-&gt;size; ix += 1)
        ctn-&gt;destroy(ctn-&gt;item[ix]);
    return NULL;
}

int ctn_insert(void* item, Container* ctn)
{  // it is not wise to insert just a pointer
    // that can be free&#39;d elsewhere:
    // it must be a copy. But an void can allocate
    // memory so we need to rely on user supplied
    // method
    if (item == NULL) return -1;       // no void
    if (ctn == NULL) return -2;        // no container
    if (ctn-&gt;size == ctn-&gt;limit)
        return -3;                     // container full
    if (ctn-&gt;copy == NULL) return -4;  // no copy function
    ctn-&gt;item[ctn-&gt;size] = ctn-&gt;copy(item);
    if (ctn-&gt;item[ctn-&gt;size] == NULL)
        return -5;  // error on copy
    ctn-&gt;size += 1;
    return 0;
}

int ctn_show(Container* col)
{
    if (col == NULL) return -1;
    printf(
        &quot;    there are %llu of %llu items\n\n&quot;, col-&gt;size,
        col-&gt;limit);
    if (col-&gt;show == NULL) return -1;  // no show function
    for (size_t i = 0; i &lt; col-&gt;size; i += 1)
        col-&gt;show(col-&gt;item[i]);
    printf(&quot;[end of listing]\n&quot;);
    return 0;
}

main.c for a simple test

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#include &quot;container.h&quot;
#include &quot;item.h&quot;

int main(void)
{
    const int  test_size = 6;
    Container* books =
        ctn_create(30, copy_book, delete_book, print_book);
    for (size_t id = 1; id &lt;= test_size; id += 1)
    {
        Book* newb = create_book();
        ctn_insert(newb, books);
        delete_book(newb);
    };
    ctn_show(books);
    printf(
        &quot;\n\t***** now print again, using new layout\n\n&quot;);
    books-&gt;show = print_book_alt;
    ctn_show(books);             // now using _alt
    books = ctn_destroy(books);  // delete all
    ctn_show(books);             // now empty
    return 0;
}

Note 1:

    ctn_show(books);
    printf(
        &quot;\n\t***** now print again, using new layout\n\n&quot;);
    books-&gt;show = print_book_alt;
    ctn_show(books);             // now using _alt

The point of using books-&gt;show here is that it encapsulates behavior, as an object in Python or java or C++: by changing this, the next call to cnt_show inherits the new print layout: see the program output below

Note 2:

    ctn_show(books);             // now using _alt
    books = ctn_destroy(books);  // delete all
    ctn_show(books);             // now empty

By returning NULL from cnt_destroy the pointer of the container is invalidaded in the same line of code: no way to have an invalid pointer. This is great, so the call to ctn-&gt;show on the next line will not get the pointer of a container already deleted.

output for the example

    there are 6 of 30 items
Book ID       : 1000
title         : Title 1000 [241 pages]
author        : Author 1000
Book ID       : 1001
title         : Title 1001 [222 pages]
author        : Author 1001
Book ID       : 1002
title         : Title 1002 [334 pages]
author        : Author 1002
Book ID       : 1003
title         : Title 1003 [350 pages]
author        : Author 1003
Book ID       : 1004
title         : Title 1004 [304 pages]
author        : Author 1004
Book ID       : 1005
title         : Title 1005 [269 pages]
author        : Author 1005
[end of listing]
***** now print again, using new layout
there are 6 of 30 items
1000, &quot;Title 1000 [241 pages]&quot;, [Author 1000]
1001, &quot;Title 1001 [222 pages]&quot;, [Author 1001]
1002, &quot;Title 1002 [334 pages]&quot;, [Author 1002]
1003, &quot;Title 1003 [350 pages]&quot;, [Author 1003]
1004, &quot;Title 1004 [304 pages]&quot;, [Author 1004]
1005, &quot;Title 1005 [269 pages]&quot;, [Author 1005]
[end of listing]

Code is on https://github.com/ARFNeto-CH/soc23-0720-example

huangapple
  • 本文由 发表于 2023年7月18日 06:56:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76708554.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定