I want to print the content of the book record as defined in the print_book()
function. Here is the code. My intention is to use the function to print any struct
of type Book.
#include <stdio.h>
#include <string.h>
void print_book(struct Book book);
struct Book
{
int id;
char *title;
char *author;
int pages;
};
int main(void)
{
struct Book book_1;
book_1.id = 23;
strcpy(book_1.title, "Java! A Complete Reference");
strcpy(book_1.author, "Ryna Dahl");
book_1.pages = 876;
print_book(book_1);
return 0;
}
void print_book(struct Book book)
{
printf("BOOK\n");
printf("Book ID: %i\n", book.id);
printf("Book Ttitle: %s\n", book.title);
printf("Book Author: %s\n", book.author);
printf("Number of Pages: %i\n", book.pages);
}
Here is the error I am receiving in the console
muyiwa@muyiwa-HP-Pro-3010-Microtower-PC:~/Documents/C$ clang -o struct_pointer struct_pointer.c
struct_pointer.c:4:24: warning: declaration of 'struct Book' will not be visible outside of this function [-Wvisibility]
void print_book(struct Book book);
^
struct_pointer.c:22:16: error: argument type 'struct Book' is incomplete
print_book(book_1);
^~~~~~
struct_pointer.c:4:24: note: forward declaration of 'struct Book'
void print_book(struct Book book);
^
struct_pointer.c:26:6: error: conflicting types for 'print_book'
void print_book(struct Book book)
^
struct_pointer.c:4:6: note: previous declaration is here
void print_book(struct Book book);
^
1 warning and 2 errors generated.
muyiwa@muyiwa-HP-Pro-3010-Microtower-PC:~/Documents/C$ clang -o struct_pointer struct_pointer.c
struct_pointer.c:4:24: warning: declaration of 'struct Book' will not be visible outside of this function [-Wvisibility]
void print_book(struct Book book);
^
struct_pointer.c:16:17: error: variable has incomplete type 'struct Book'
struct Book book_1;
^
struct_pointer.c:16:12: note: forward declaration of 'struct Book'
struct Book book_1;
^
struct_pointer.c:26:24: warning: declaration of 'struct Book' will not be visible outside of this function [-Wvisibility]
void print_book(struct Book book)
^
struct_pointer.c:26:6: error: conflicting types for 'print_book'
void print_book(struct Book book)
^
struct_pointer.c:4:6: note: previous declaration is here
void print_book(struct Book book);
^
struct_pointer.c:26:29: error: variable has incomplete type 'struct Book'
void print_book(struct Book book)
^
struct_pointer.c:26:24: note: forward declaration of 'struct Book'
void print_book(struct Book book)
^
2 warnings and 3 errors generated.
I have tried all possible means, and on checking other sources, my code looks correct.
CodePudding user response:
Scopes of Declarations
When something is newly declared in a function parameter, its scope (the portion of source code in which it is visible) is limited to the function declaration. So, in void print_book(struct Book book);
, struct Book
declares a type that is known only inside that declaration. When the definition of struct Book
appears later, it is a different struct Book
.
If you put the definition of struct Book
first, outside of any function, then its scope is the rest of the translation unit (the file being compiled along with any files it includes with #include
). Then, when
void print_book(struct Book book);appears later, this
struct Book` will not define a new type; it will refer to the type that is already in scope.
Lesson: Never make struct Name
inside a parameter declaration the first appearance of struct Name
in the code.
Note that you do not need to fully define the structure first. Merely putting a struct Book;
first would inform the compiler there is a type struct Book
with file scope. You could put the definition later (but before the compiler needs to know about the members of the structure).
Allocation of Memory
In this code:
strcpy(book_1.title, "Java! A Complete Reference");
strcpy(book_1.author, "Ryna Dahl");
no memory has yet been allocated for book_1.title
or book_1.author
. These members are merely pointers that have not been given a value. To use strcpy
correctly, you must allocate memory for the strings and set the pointers to point to that memory.
You can just set the pointers to point to the string literals directly:
book_1.title = "Java! A Complete Reference";
book_1.author = "Ryna Dahl";
That sets the members to point to strings that you should treat as constants. If you want to have modifiable memory, you could do this:
// Make temporary pointers to the string literals.
char *Title = "Java! A Complete Reference";
char *Author = "Ryna Dahl";
// Allocate memory for copies, including the terminating null characters.
book_1.title = malloc(strlen(Title) 1);
book_1.author = malloc(strlen(Author) 1);
if (!book_1.title || !book_1.author)
{
fprintf(stderr, "Error, unable to allocate memory.\n");
exit(EXIT_FAILURE);
}
// Copy the strings into the allocated memory.
strcpy(book_1.title, Title);
strcpy(book_1.author, Author);
Generally, if you allocate memory in this way, you should call free
to release it when you are done using it. (In general-purpose multiuser operating systems, this can be omitted when the program is terminating, as the operating system will reclaim all memory.)
CodePudding user response:
#include <stdio.h>
#include <string.h>
#define TITLE_SIZE (128)
#define AUTHOR_SIZE (128)
struct Book
{
int id;
int pages;
char title[TITLE_SIZE];
char author[AUTHOR_SIZE];
};
void print_book(struct Book book);
int main(void)
{
struct Book book_1;
book_1.id = 23;
strcpy(book_1.title, "Java! A Complete Reference");
strcpy(book_1.author, "Ryna Dahl");
book_1.pages = 876;
print_book(book_1);
return 0;
}
void print_book(struct Book book)
{
printf("BOOK\n");
printf("Book ID: %i\n", book.id);
printf("Book Ttitle: %s\n", book.title);
printf("Book Author: %s\n", book.author);
printf("Number of Pages: %i\n", book.pages);
}