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

标签 c segmentation-fault

我正在尝试创建一个记录,我可以在其中跟踪学生的姓名和分数。运行后,我输入 1 个学生记录,输入 2 个记录后,出现段错误。我不明白是什么导致了这个错误,因为我是 C 语言的初学者。这是代码:

#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;
}

最佳答案

虽然您对段错误 的起源有解释,但如果您打算学习 C,请不要过早养成坏习惯。在您认为您的代码可靠之前,您应该解决许多问题,并且您可以解决许多其他问题以使您的代码像您预期的那样工作。

首先,验证所有用户输入!。如果您未能验证所有用户输入,您将不知道您的代码是在处理垃圾还是从您接受的第一个输入开始偏离未定义的行为。用户可以输入任何内容——或者一只猫可以在键盘上行走,等等……您有责任验证您是否获得了您期望的输入。

例如,从您的第一个输入开始,您应该至少检查scanf 的返回以确保您有成功转换的数量em> 你期望。这很简单,例如

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

当进行字符串输入时,您需要将接受的字符数限制为可用存储空间的大小。您可以通过使用 width 修饰符的 scanf 来完成此操作。由于 name 中有 100 个字符可用,因此您可以存储 99 个字符 加上 nul-terminating byte。此外,由于名称可以包含空格,您可以使用字符类 来读取直到'\n' 字符的所有字符。 (在这里,您最好使用 面向行 的输入函数,例如 fgets)。例如

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

您还需要了解您将 '\n' 留在输入缓冲区 (stdin) 中,您必须考虑到这一点如果您的下一个输入是 scanf 的字符输入,将很乐意将 '\n' 作为您的输入。所有面向行的输入函数(fgetsgetline)读取并包含'\n'scanf不.但是,您可以使用赋值抑制运算符'*'读取并丢弃指定的输入(%*c 读取/丢弃下一个字符('\n')。

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向起始地址的指针内存块,(2) 当不再需要时可以释放。例如

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

您必须使用内存错误检查程序来确保您没有在分配的内存块之外/之外写入,尝试读取或基于未初始化的值进行跳转,最后确认您已释放所有你分配的内存。对于 Linux valgrind 是正常的选择,但每个操作系统都有类似的程序。

最后,虽然不是错误,但 C 语言的标准编码风格避免了 caMelCase 变量,转而使用所有小写。参见例如NASA - C Style Guide, 1994

将所有这些放在一起,并根据您的预期调整输出和格式(我可能完全错了),您可以重写代码,例如:

#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;
}

示例使用/输出

$ ./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

查看所有答案和评论,如果您有任何问题,请告诉我。

关于c - 获取段错误而不是打印结构值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37871441/

相关文章:

c - 使用多线程的段错误

c - C函数是否保证有固定的内存地址?

c - 结构内部指向动态二维数组的指针

c - 使用按位运算符的集合中的下一组 n 个元素

c - 增加结构段错误的成员

Linux C 和 C++ : what else should I be logging when handling signals like SIGSEGV?

c - X11 中的滚轮事件

c - 读取函数: copying buffer,重新分配内存

c - 在C中输出文件时出现段错误(核心转储)错误

c++ - 段错误澄清