0

I'm trying to read information about students using scanf() in a for loop (N times -> N inputted by the user), but the for() loop goes more than that... it doesn't stop at all. I've tried all the combinations for the string formatting, but nothing seems to work.

This is my code:

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

typedef struct STUDENT_CONSTRUCTOR {
    char first_name[35], last_name[35];
    char gender;
    int grade;
} student;

int main(void) {
    int N; // number of entries

    student list[N];

    printf("Number of entries: "); scanf("%d", &N);

    for (int i = 0; i<N; i++) {
        printf("<Entry %d>\n", i);
        scanf("%d %c %s %s\n", &list[i].grade, &list[i].gender, list[i].first_name, list[i].last_name);
    }
    return 0;
}

What I want to type in are sequences like the following: 10 M Mateas Mario, where "10" is the average grade, "M" the gender, "Mario" my first name and "Mateas", my last name.

N is properly read, and also the data inside the list[] array is properly read. list[] is based on a structure that holds all these data types.

Still, whenever I type in a specific value for N (for example 3, 4, 5, etc.), <Entry ...> always goes beyond that number. Before telling me that scanf() is deprecated, I would like to know if there's a possibility of fixing this code without getting rid of the scanf() function. I don't want to use fgets() because I don't know the maximum number of characters the input contains.

Any help would be appreciated. Thank you.

Mario Mateaș
  • 638
  • 7
  • 25
  • 2
    `scanf()` isn't deprecated, but some coders prefer not to use it. But [there are ways](https://www.google.co.uk/search?q=how+to+input+a+string+of+unknown+length+in+c+site:stackoverflow.com) to use `fgets()` when you don't know the line length in advance – Weather Vane Oct 27 '21 at 18:11
  • And `scanf()` is often problemmatic, but I don't see any reason to think that it's responsible for the issue here. – John Bollinger Oct 27 '21 at 18:14
  • 6
    Ah. What do you think `student list[N];` does when it appears *before* a value for `N` has been entered? – John Bollinger Oct 27 '21 at 18:16
  • @JohnBollinger Oh, there was the problem. Can you post it as an answer, so I can mark it as solved? Thank you so much :) My bad – Mario Mateaș Oct 27 '21 at 18:17
  • Undefined behavior, voting to close as a typo. – Mark Tolonen Oct 27 '21 at 18:19
  • Another typical problem with using `scanf` and `%s` is when the first or last name consists of two words, such as 'Betty Sue Smith' or 'Peter Da Silva'. – Weather Vane Oct 27 '21 at 18:19

2 Answers2

2

Here ...

    int N; // number of entries

    student list[N];

... you are using the indeterminate initial value of N to specify the dimension of list. Undefined behavior results. That UB is not constrained to manifest as an immediate failure or a noisy one. In this particular case, it is manifesting as your iteration not stopping where you expect it to do.

Although it is not generally very fruitful to reason about the mechanism of a particular manifestation of UB, it nevetherless seems commonly to inspire some kind of perverse curiosity, so I will speculate that in your case, list is being given fewer elements than ultimately you request, maybe even 0, and as a result, your inputs are overrunning its bounds, causing the value of N to change unexpectedly.

Fix it by inputting N before the declaration of list, or else by just declaring list with a fixed number of elements large enough to accommodate every acceptable value of N.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

You have used the non-initialized variable N in a declaration of a variable length array

int N; // number of entries

student list[N];

So the program has undefined behavior.

Instead you need to write

int N; // number of entries

printf("Number of entries: "); scanf("%d", &N);

student list[N];

Also in the call of scanf remove the new line character '\n' from the format string

scanf("%d %c %s %s", &list[i].grade, &list[i].gender, list[i].first_name, list[i].last_name);

And instead of the function scanf you may use the function scanf_s.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • *And instead of the function `scanf` you may use the function `scanf_s`.* That's a lot less portable and not really any safer. – Andrew Henle Oct 27 '21 at 18:43
  • @AndrewHenle Write your remark to the C Standard Committee. It could be useful for you yourself. – Vlad from Moscow Oct 27 '21 at 18:49
  • There's an entire question with answers: [**Why didn't gcc (or glibc) implement _s functions?**](https://stackoverflow.com/questions/50724726/why-didnt-gcc-or-glibc-implement-s-functions). Also [**Field Experience With Annex K — Bounds Checking Interfaces**](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm). TLDR: non-portable, slow, not thread-safe, opens up programs to new attacks so really is *less* safe because it allows them to keep running in an indeterminate state after an error. – Andrew Henle Oct 27 '21 at 19:33
  • @AndrewHenle So is scanf more safe? Then why is the compiler saying that scanf is deprecated? – Vlad from Moscow Oct 27 '21 at 19:35
  • [Ummm, who's ***lying*** to you about `scanf()` being "deprecated"?](https://port70.net/~nsz/c/c11/n1570.html#7.21.6.4) – Andrew Henle Oct 27 '21 at 19:38
  • @AndrewHenle This is written in the question.:) – Vlad from Moscow Oct 27 '21 at 19:39