简体   繁体   English

获取段错误而不是打印结构值

[英]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. 运行后,我输入了1条学生记录,在输入了2条记录后,我遇到了分段错误错误。 I don't understand what is causing this error as I am a beginner to the C language. 我不了解是什么原因导致此错误,因为我是C语言的初学者。 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. 当你对你有段错误起源地解释,如果你要学C,不早退学坏习惯。 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. 例如,从您的第一个输入开始,您至少应检查scanf的返回值,以确保您拥有期望的成功转换次数。 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. 您可以使用width修饰符使用scanf完成此操作。 Since you have 100 characters available in name , you can store 99 chars plus the nul-terminating byte . 由于name100字符可用,因此您可以存储99 chars以及nul-terminating byte Additionally, since names can contain whitespace, you can use a character class to read all characters up to the '\\n' character. 此外,由于名称可以包含空格,因此您可以使用字符类来读取所有字符,直到'\\n'字符为止。 (here you are better served using line-oriented input functions such as fgets ). (这里最好使用诸如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. 您还需要了解您将'\\n'保留在输入缓冲区stdin )中,并且如果下一个输入是scanf字符输入,则必须考虑到这一点,我们很乐意将'\\n'作为输入。 All line oriented input functions ( fgets , getline ) read and include the '\\n' , scanf does not. 所有面向行的输入函数( fgetsgetline )均读取并包括'\\n'scanf则不包括。 You can however use the assignment suppression operator '*' to read-and-discard the specified input ( %*c to read/discard the next character (the '\\n' )). 但是,您可以使用赋值抑制运算符'*'读取和丢弃指定的输入( %*c来读取/丢弃下一个字符( '\\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. 在您的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个责任 :(1) 始终保留指向该内存块的起始地址的指针,因此,(2)在没有内存块时可以将其释放需要更长的时间。 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. 对于Linux, valgrind是通常的选择,但是每个OS都有类似的程序。

Last, while not an error, the standard coding style for C avoids caMelCase variables in favor of all lower-case . 最后,尽管不是错误,但C的标准编码样式避免了caMelCase变量,而建议使用所有小写字母 See eg NASA - C Style Guide, 1994 参见例如NASA-C样式指南,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. 查看所有答案和评论,如果您有任何问题,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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