How to avoid This Segmenation Fault Error in Structure

65 views Asked by At

This is my code:

#include <stdio.h>
struct book
{
    char title[30];
    char author[30];
    char isbn[30];
    float price;
}books[5];
int main()
{
    int i=1,j=0;
    while(i<6)
    {
        printf("Enter Title of Book No %d : ",i);
        scanf("%[^\n]s",&books[j].title);
        printf("Enter Author of Book No %d : ",i);
        gets("%s",books[j].author);
        i++;
        j++;
    }
    printf("%s",books[3].author);
}

I keeps on getting segmentation fault while running the program. Any solution?

I first used gets and then tried Scanf instead of gets. I alsot tried declaring structure variable in the main function.

This is the error I'm getting:

$ ./a.out      
Enter Title of Book No 1 : ttn ttr
zsh: segmentation fault  ./a.out
                                     

Tried Using This code

#include <stdio.h>
struct book
{
    char title[30];
    char author[30];
    char isbn[30];
    float price;
}book;
int main()
{
    int i=1,j=0;
    struct book books[5];
    while(i<6)
    {
        printf("Enter Title of Book No %d : ",i);
        scanf("%[^\n]s",books[j].title);
        printf("Enter Author of Book No %d : ",i);
        scanf("%[^\n]s",books[j].author);
        printf("Enter ISBN of Book no %d : ",i);
        scanf("%[^\n]s",books[j].isbn);
        printf("Enter Price of Book no %d : ",i);
        scanf("%f",&books[j].price);
        i++;
        j++;
    }
    printf("%s",books[3].author);
}

but this only reads first scanf()

output :

Enter Title of Book No 1 : ads
Enter Author of Book No 1 : Enter ISBN of Book no 1 : Enter Price of Book no 1 : 54
Enter Title of Book No 2 : Enter Author of Book No 2 : Enter ISBN of Book no 2 : Enter Price of Book no 2 : 564
Enter Title of Book No 3 : Enter Author of Book No 3 : Enter ISBN of Book no 3 : Enter Price of Book no 3 : ^C

2

There are 2 answers

6
New Leaf On

First of all, gets only take one argument.

Second of all, when scanf a string, you don't need to add &, because & means the address of. However, string is already passed by its address.

You should use fgets(char *str, int n, FILE *stream) instead of gets, fgets can tell the program the char limit by n and where to read the input. For example : if you would like to read the input in stdin and the string limit is 50, you can use fgets(string, 50, stdin). Moreover, you should notice that fgets automatically add "\n" in the end of string, you can use string[strcspn(string,"\n")] = 0 to get rid of the "\n".

The following modification works well in my device.

#include <stdio.h>
typedef struct book
{
    char title[30];
    char author[30];
    char isbn[30];
    float price;
}book;
book books[5];


int main()
{
    int i=0,j=0;
    while(i<5)
    {
        printf("Enter Title of Book No %d : ",i);
        scanf("%[^\n]s", books[j].title);
        printf("Enter Author of Book No %d : ",i);
        gets(books[j].author);
        i++;
        j++;
    }
    printf("%s",books[3].author);


}
0
Gerhardh On

You combine multiple issues in your code.

  1. You use deprecated function gets. If I compile your code, I get a warning:
> gcc -Wall -Wextra -pedantic -o test test.c
test.c: In function ‘main’:
test.c:18:7: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
   18 |       gets("%s",books[j].author);
      |       ^~~~
      |       fgets

If you use some compiler version with ancient version, you should get at least a warning about your arguments:

gcc -Wall -Wextra -pedantic --std=c99 -o test test.c
test.c: In function ‘main’:
test.c:18:7: warning: ‘gets’ is deprecated [-Wdeprecated-declarations]
   18 |       gets("%s",books[j].author);
      |       ^~~~
In file included from test.c:1:
/usr/include/stdio.h:605:14: note: declared here
  605 | extern char *gets (char *__s) __wur __attribute_deprecated__;
      |              ^~~~
test.c:18:7: error: too many arguments to function ‘gets’
   18 |       gets("%s",books[j].author);
      |       ^~~~
In file included from test.c:1:
/usr/include/stdio.h:605:14: note: declared here
  605 | extern char *gets (char *__s) __wur __attribute_deprecated__;
      |              ^~~~

This should ring some bells that you should not use it at all or at least should not use it that way.

If you do not get these warnings (due to insufficient warning leven) you are causing undefined behaviour because you pass wrong arguments:

       gets("%s",books[j].author);

You pass "%s" instead of the address of a buffer where the input should be stored. This is a string literal which means you mustn't modify it. Hence, gets cannot store any input at that address which is the reason why you get that segfault.


  1. You should generally not mix scanf and gets or fgets as they may mess up buffer handling.

Either use scanf all the time or use fgets followed by sscanf.


  1. Your format strings allow buffer overflow: You should always provide a length limit when you read strings: Your buffers are 30 characters long, that means you can read strings up to 29 characters:
  scanf("%29[^\n]s",books[j].author);

  1. Your format strings for scanf are generally wrong:
   scanf("%[^\n]s",books[j].author);

The s is not part of format specifier. That means, scanf reads everything up to \n and then fails to consume another s because it does not match.

This will keep \n in your input buffer.

When you call scanf again with similar string, the first character in the buffer is \n which will terminate your input immediately.

You need to get rid of that newline:

If you still want to use scanf, use one of these:

  scanf("%29[^\n]\n",books[j].author);
  scanf("%29[^\n]%*c",books[j].author);

This will consume the \n for the calls to read strings. But also after reading your price, a \n will be kept in buffer which will fail in next iteration of your loop.


  1. Keeping 2 loop counters is error prone

You should not keep i and j just to handle the offset of 1 for printing.


To keep better control about your input, you should replace all your input with fgets and sscanf.

A fixed version could look like this:

#include <stdio.h>

struct book
{
  char  title[30];
  char  author[30];
  char  isbn[30];
  float price;
} book;

int main(void)
{
    int j=0;
    struct book books[5];
    char buffer[31];

    while (j < 5)
    {
        printf("Enter Title of Book No %d : ", j+1);
        fgets(buffer, sizeof(buffer), stdin);
        sscanf(buffer, "%29[^\n]",books[j].title);

        printf("Enter Author of Book No %d : ", j+1);
        fgets(buffer, sizeof(buffer), stdin);
        sscanf(buffer, "%29[^\n]",books[j].author);

        printf("Enter ISBN of Book no %d : ", j+1);
        fgets(buffer, sizeof(buffer), stdin);
        sscanf(buffer, "%29[^\n]", books[j].isbn);

        printf("Enter Price of Book no %d : ", j+1);
        fgets(buffer, sizeof(buffer), stdin);
        sscanf(buffer, "%f", &books[j].price);
        j++;
    }
    printf("%s", books[3].author);
}