简体   繁体   中英

Getting a seg fault rather than printing the struct value

I am trying to create a record in which I can keep track of students' names and their scores. after running, I enter 1 student record and after entering 2 records, I am getting a segmentation fault error. I don't understand what is causing this error as I am a beginner to the C language. here is the code:

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

typedef struct Students {
    char name[100];
    int roll;
    float mark;
} Student;


int main() {
    int N,i;
    printf("How many students do you want to enter: ");
    scanf("%i",&N);
    Student *st = malloc(N*sizeof(Student));
    for (i = 0; i < N; i++){
        printf("Enter name: ");
        scanf("%s",st[i].name);
        st[i].roll = i;
        printf("Enter score for %s", st[i].name);
        scanf("%f",&st[i].mark);
        printf("%i. %s ",i,st[i].name);
        printf("%s: %f ",i,&st[i].mark);
        printf("\n");
    }

   return 0;
}

Using a debugger and backtracking at the core dump, you get that it crashes here;

printf("%s: %f ",i,&st[i].mark);

The line should probably look something like this instead;

printf("%d: %f ",i,st[i].mark);

While you have an explanation for where you segfault originated, if you are going to learn C, do not learn bad habits early. There are a number of issues you should address before you consider your code reliable, and a number of others you can address to make your code work like it appears you intended.

First and foremost, validate ALL user input! . If you fail to validate all user input you have no idea if your code is processing garbage or straying off into undefined behavior from the first input you take. A user can input anything -- or a cat can walk on the keyboard, etc... It is your responsibility to verify you have the input you are expecting.

For example, from your very first input, you should, at minimum check the return of scanf to insure you have the number of successful conversions you expect. It is simple to do, eg

if (scanf ("%d", &n) != 1) {    /* validate number */
    fprintf (stderr, "error: invalid input (number).\n");
    return 1;
}

When taking string input you need to limit the number of characters you accept to the size of the storage available. You can accomplish this with scanf using the width modifier. Since you have 100 characters available in name , you can store 99 chars plus the nul-terminating byte . Additionally, since names can contain whitespace, you can use a character class to read all characters up to the '\\n' character. (here you are better served using line-oriented input functions such as fgets ). eg

    if (scanf (" %99[^\n]%*c", st[i].name) != 1) {
        fprintf (stderr, "error: invalid input (name).\n");
        return 1;
    }

You also need to understand you are leaving the '\\n' in the input buffer ( stdin ) and you must account for that if your next input is character input of scanf will happily take the '\\n' as your input. All line oriented input functions ( fgets , getline ) read and include the '\\n' , scanf does not. You can however use the assignment suppression operator '*' to read-and-discard the specified input ( %*c to read/discard the next character (the '\\n' )).

In any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. eg

free (st);  /* free allocated memory */

It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an uninitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind is the normal choice, but there are similar programs for each OS.

Last, while not an error, the standard coding style for C avoids caMelCase variables in favor of all lower-case . See eg NASA - C Style Guide, 1994

Putting all of that together, and tweaking the output and formatting to what it seems you intended (I could be totally wrong), you could rewrite you code something like:

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

typedef struct students {
    char name[100];
    int roll;
    float mark;
} student;

int main ()
{
    int n = 0, i = 0, maxc = 0; /* initialize variables */
    student *st = NULL;

    printf ("How many students do you want to enter: ");
    if (scanf ("%d", &n) != 1) {    /* validate number */
        fprintf (stderr, "error: invalid input (number).\n");
        return 1;
    }
    /* allocate & validate */
    if ((st = malloc (n * sizeof (student))) == NULL) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    } 

    for (i = 0; i < n; i++) {       /* take input & validate */
        printf ("enter name: ");    /* limit & accept full name */
        if (scanf (" %99[^\n]%*c", st[i].name) != 1) {
            fprintf (stderr, "error: invalid input (name).\n");
            return 1;
        }
        st[i].roll = i;

        printf ("enter score for %s: ", st[i].name);
        if (scanf ("%f", &st[i].mark) != 1) {
            fprintf (stderr, "error: invalid input (mark).\n");
            return 1;
        }
    }

    for (i = 0; i < n; i++) {   /* compute max length for name */
        int len = (int)strlen (st[i].name);
        if (len > maxc)
            maxc = len;
    }

    printf ("\nroll  %-*s  mark\n\n", maxc, "name");
    for (i = 0; i < n; i++)
        printf (" %3d  %-*s  %.2f\n",
                st[i].roll, maxc, st[i].name, st[i].mark);

    free (st);  /* free allocated memory */

    return 0;
}

Example Use/Output

$ ./bin/structloop
How many students do you want to enter: 4
enter name: John J. Franklin
enter score for John J. Franklin: 83.1
enter name: Betty C. Smith
enter score for Betty C. Smith: 91.2
enter name: Jennifer L. Burgen-Kwiatkowski
enter score for Jennifer L. Burgen-Kwiatkowski: 88.7
enter name: Alfred R. Murrow
enter score for Alfred R. Murrow: 73.5

roll  name                            mark

   0  John J. Franklin                83.10
   1  Betty C. Smith                  91.20
   2  Jennifer L. Burgen-Kwiatkowski  88.70
   3  Alfred R. Murrow                73.50

Look over all the answers and comments and let me know if you have any questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM