简体   繁体   English

为什么当我在学期变量中输入正确的值时,我的程序会崩溃? C

[英]why when i put a proper value in semester variable my program crashes? C

struct student{
    char *number;
    char *name;
    int semester;
    float *grades;
};

typedef struct student RECORD;

void read_record(RECORD *p);
void print_record(RECORD x);
void init_record(RECORD *p);
void free_record(RECORD x);

main()
{
    int N;
    printf("Please enter the number of students that you want to insert: ");
    scanf("%d",&N);

    RECORD array[N];
    int i;

    for (i=0; i<N; i++)
        init_record(&array[i]);

at this point, when i put the semester value from the keyboard my program crashes此时,当我从键盘输入学期值时,我的程序崩溃了

for (i=0; i<N; i++)
{
    printf("Number %d student: \n",i+1);
    read_record(&array[i]);     
}


for (i=0; i<N; i++)
print_record(array[i]);


    for (i=0; i<N; i++)
    {
        free_record(array[i]);
    }   
}

I tried to remove any unnecessary code but I left the most important parts so any false part would be easier to be seen我试图删除任何不必要的代码,但我留下了最重要的部分,因此任何错误的部分都更容易被看到

void read_record(RECORD *p)
{
        int i;

    printf("Please give the name: ");
    scanf("%s", p->name);

    printf("Please give AM number: ");
    scanf("%s", p->number);

    printf("Please give semester: ");
    scanf("%d",p->semester);

    printf("Please give grades: ");
    for(i=0;i<5;i++)
    scanf("%f", &(p->grades[i]));
}

After this piece of code I have the init_record function that includes mallocs for struct's arrays在这段代码之后,我有init_record函数,其中包括结构数组的 malloc

void init_record(RECORD *p)
{

    p->name = malloc(sizeof(char)*40);
    if (!p->name)
    {
        printf("Error!");
        exit(0);
    }

    p->number = malloc(sizeof(char)*6);
    if (!p->number)
    {
        printf("Error!");
        exit(0);
    }

    p->grades = malloc(sizeof(float)*5);
    if (!p->grades)
    {
        printf("Error!");
        exit(0);
    }
}

Assuming that you've allocated the memory for the struct members假设您已经为结构成员分配了内存

printf("Please give semester: ");
scanf("%d",p->semester);

is the problem, as p->semester is an int .是问题所在,因为p->semester是一个int %d expects a pointer to int , you are passing an uninitialized integer value as a pointer to scanf . %d需要一个指向int的指针,您将一个未初始化的整数值作为指向scanf的指针传递。

The correct call would be正确的调用是

scanf("%d", &p->semester);

Usually once uses malloc because one need objects that live outside the scope of function, for example with linked lists, trees, etc. But also when you need an array whose dimension is not known on compiler time.通常一次使用malloc是因为需要存在于函数范围之外的对象,例如链表、树等。但是当您需要一个在编译器时维度未知的数组时也是如此。

Your init_record seems to me pointless, because you know the size of the arrays and they are even small, you could easily change your struct to您的init_record在我看来毫无意义,因为您知道数组的大小,而且它们甚至很小,您可以轻松地将结构更改为

struct student {
    char number[6];
    char name[40];
    int semester;
    float grades[5];
};

and then you wouldn't need to do the malloc calls.然后你就不需要做malloc调用了。

Also, you would need to do the scanf like this:此外,您需要像这样执行scanf

int c;国际 c;

scanf("%39s", p->name);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer

scanf("%5s", p->number);
while((c = getchar()) != '\n' && c != EOF); // clearing the buffer

...

to prevent buffer overflows if the user enters a name/number that are too large for the buffer.如果用户输入的名称/号码对于缓冲区来说太大,则可以防止缓冲区溢出。


edit编辑

OP asked in the comments OP 在评论中询问

One last thing: I wanted to replace scanf with gets in the case of name, because I want a name and a surname to be saved as one piece(including space).最后一两件事:我想替换scanf函数与gets的名字的情况下,因为我想一个名字和姓氏保存为一个整体(包括空格)。 But when I replaced scanf, I got this error: request for member 'name' in something not a structure or union.但是当我替换 scanf 时,我收到了这个错误:在不是结构或联合的东西中请求成员“名称”。 I searched over here for a similar problem but I didn't find any solution.我在这里搜索了类似的问题,但没有找到任何解决方案。 Can you suggest one?你能推荐一个吗?

Don't use gets , this is a dangerous function because it doesn't take the size of the array into account and if the entered text is larger than the buffer size, it will overflow and cause a lot of damage.不要使用gets ,这是一个危险的函数,因为它没有考虑数组的大小,如果输入的文本大于缓冲区大小,它会溢出并造成很大的损坏。 The error message is due to a syntax error, as you don't show the code, I cannot say what you did wrong.错误消息是由于语法错误造成的,因为您没有显示代码,我无法说出您做错了什么。 However, if you take my advice, you won't have this error.但是,如果您接受我的建议,就不会出现此错误。

In general I advice not to use scanf to read from the user, because scanf was not designed to do so.一般来说,我建议不要使用scanf来读取用户的信息,因为scanf不是为了这样做而设计的。 Specially when you want to read strings that have empty spaces, it's better to read the whole line with fgets and parse the line later using other functions like strchr , strstr , strtok , sscanf , strtol , etc. Which function to use depends on what you are trying to read from the user.特别是当你想读取有空格的字符串时,最好用fgets读取整行,然后使用其他函数解析该行,如strchrstrstrstrtoksscanfstrtol等。使用哪个函数取决于你正在尝试从用户那里读取。 In this case where you are reading strings, fgets would give better results.在这种情况下,您正在读取字符串, fgets会给出更好的结果。 So I'd change your whole reading process to this:所以我会把你的整个阅读过程改成这样:

int read_record(RECORD *p)
{
    char line[1024];

    printf("Please give the name: ");
    if(fgets(line, sizeof line, stdin) == NULL)
    {
        fprintf(stderr, "Could not read name\n");
        return 0; // error
    }

    line[strcspn(line, "\n")] = 0; // removing newline
    strncpy(p->name, line, sizeof p->name);
    p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string

    printf("Please give AM number: ");
    if(fgets(line, sizeof line, stdin) == NULL)
    {
        fprintf(stderr, "Could not read AM number\n");
        return 0;
    }

    line[strcspn(line, "\n")] = 0; // removing newline
    strncpy(p->number, line, sizeof p->name);
    p->name[sizeof(p->number) - 1] = 0; // making sure to get a valid string

    // this is ok, this can stay like this
    printf("Please give semester: ");
    scanf("%d", &p->semester);

    printf("Please give grades: ");
    if(fgets(line, sizeof line, stdin) == NULL)
    {
        fprintf(stderr, "Could not read grades\n");
        return 0;
    }

    if(sscanf(line, "%f %f %f %f %f",
                p->grades, p->grades + 1, p->grades + 2, p->grades + 3,
                p->grades + 4) != 5)
    {
        fprintf(stderr, "Could not read 5 grades\n");
        return 0;
    }

    return 1; // success
}

I know that it is much more code that you have before, but this code is more robust, it handles cases where the user did input the wrong format, your code can react to that, print error messages, retry the user input, whatever.我知道您以前拥有的代码要多得多,但此代码更健壮,它可以处理用户输入错误格式的情况,您的代码可以对此做出反应,打印错误消息,重试用户输入,等等。 With your the error manifest themselves when the data printed on screen or a file is strange.当屏幕或文件上打印的数据很奇怪时,错误就会显现出来。

Note how I copied the string:请注意我是如何复制字符串的:

strncpy(p->name, line, sizeof p->name);
p->name[sizeof(p->name) - 1] = 0; // making sure to get a valid string

Here I assuming that you've changed the struct to hold arrays like I said in the first part of the answer.在这里,我假设您已经更改了结构以保存数组,就像我在答案的第一部分中所说的那样。 If you didn't change that and you are still using the old way by malloc ing with a hardcoded fixed size, you will have to use the hardcoded fixed size here as well:如果您没有更改它并且您仍然通过使用硬编码固定大小的malloc使用旧方法,则您也必须在此处使用硬编码固定大小:

strncpy(p->name, line, 40);
p->name[39] = 0; // making sure to get a valid string

And you see why I prefer when the struct has the array, because with sizeof I can get the size regardless of the dimension.并且您明白为什么我更喜欢结构具有数组时的原因,因为使用sizeof我可以获得大小而不管维度如何。

The other thing to notice here is that I used strncpy instead of strcpy .这里要注意的另一件事是我使用了strncpy而不是strcpy strcpy suffers from the same problems as gets , it doesn't take the size of the destination buffer into consideration and if the source string is larger than the destination buffer, it will overflow the buffer. strcpy遇到与gets相同的问题,它没有考虑目标缓冲区的大小,如果源字符串大于目标缓冲区,它将溢出缓冲区。

strncpy works like strcpy except that you pass how many bytes there is available for the destination buffer. strncpy工作方式与strcpy类似,只是您传递了目标缓冲区可用的字节数。 If the source is larger than that number, then strncpy will not writing bytes in the destination, thus preventing a buffer overflow.如果源大于该数字,则strncpy不会在目标中写入字节,从而防止缓冲区溢出。 Of course if the '\\0' -terminating byte is not among the copied bytes, it won't be written in the destination buffer.当然,如果'\\0'终止字节不在复制的字节中,则不会写入目标缓冲区。 p->name[39] = 0; just makes sure that the string is '\\0' -terminated, no matter how long the source was.只需确保字符串以'\\0'结尾,无论源有多长。

暂无
暂无

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

相关问题 当我尝试使用 C 中的 sqlite 读取 NULL 值时,我的程序崩溃了 - My program crashes when I try to read a NULL value using sqlite in C 为什么当我声明这两个变量时我的程序崩溃了? - Why my program crashes when I declare this two variables? 当if(可变%2 == 0)时程序崩溃 - Program crashes when `if (variable % 2 == 0)` 为什么当我声明一个 int 变量时,我的应用程序会冻结? C程序 - why when I declare an int variable my aplication freezes? C program 当我在代码顶部添加for循环时,程序崩溃。 为什么? - When I add a for loop to the top of my code, my program crashes. Why? 尝试在函数内添加节点但在main函数中工作时,为什么我的程序崩溃 - Why does my program crashes when I tried adding a node inside a function but works in the main function 当我尝试在C中的简单地图上跨屏幕移动播放器时,程序崩溃 - Program crashes when I try to move my player across the screen on a simple map in C 当我尝试将结构中的char *类型的元素设置为特定字符串时,C程序崩溃? - C program crashes when I tried to set an element of type char* in my struct to a specific string? 为什么我的变量在我不改变它的时候改变了它的值? C - Why my variable changes its value when I am not changing it? C 谁能告诉我为什么当我尝试在菜单驱动的数组操作程序中调用 Insert 或 Delete 函数时我的程序会崩溃? - Can anyone tell me why my program crashes when I try to call the Insert or Delete function in a menu driven array manipulation program?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM