0

So my code is supposed to read a file and print out what was read, while organizing it into a data struct, however the code just stops running after the function is completed.

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

typedef struct{
    int StudentID; 
    float ProvaIngresso;
    float NotaSecundario;
    float NotaCandidatura;
    int Escolha1, Escolha2, Escolha3, Escolha4, Escolha5;
    char curso1[4], curso2[4], curso3[4], curso4[4], curso5[4];
}Candidato;

void ColocaCandidatos(Candidato *candidatos);
int main() {


    Candidato candidatos[60000];

    ColocaCandidatos(&candidatos[60000]);

    int x = 1;
    int y = 2;
    int z = x +y;
    printf("%d",z);
  return 0;
}

void ColocaCandidatos(Candidato *candidatos){
    FILE *fp;
    char line[100];
    char *token;
    int line_count = 0;
    int i= 0; //token pa controlar numero do candidato;

    // Open the CSV file for reading
    fp = fopen("Candidatos_N10_C20_O05.csv", "r");

    if (fp == NULL) {
        printf("Error: could not open file\n");
        exit(1);
    }

    // Read each line of the file and extract the string
    while (fgets(line, 100, fp) != NULL) {

        if (line_count > 0) {  // Skip the first line
            token = strtok(line, ",");
            candidatos[i].StudentID = atoi(token);
            printf("\n%d",candidatos[i].StudentID);



            token = strtok(NULL, ",");
            candidatos[i].ProvaIngresso = atof(token);
            printf("\n%f",candidatos[i].ProvaIngresso);

            token = strtok(NULL, ",");
            candidatos[i].NotaSecundario = atof(token);
            printf("\n%f",candidatos[i].NotaSecundario);

            token = strtok(NULL, ",");
            candidatos[i].NotaCandidatura = atof(token);
            printf("\n%f",candidatos[i].NotaCandidatura);

            token = strtok(NULL, ",");
            candidatos[i].Escolha1 = atoi(token);
            printf("\n%d",candidatos[i].Escolha1);


            token = strtok(NULL, ",");
            strcpy(candidatos[i].curso1,token);
            printf("\n%s",candidatos[i].curso1);


            token = strtok(NULL, ",");
            candidatos[i].Escolha2 = atoi(token);
            printf("\n%d",candidatos[i].Escolha2);


            token = strtok(NULL, ",");
            strcpy(candidatos[i].curso2,token);
            printf("\n%s",candidatos[i].curso2);

            token = strtok(NULL, ",");
            candidatos[i].Escolha3 = atoi(token);
            printf("\n%d",candidatos[i].Escolha3);


            token = strtok(NULL, ",");
            strcpy(candidatos[i].curso3,token);
            printf("\n%s",candidatos[i].curso3);

            token = strtok(NULL, ",");
            candidatos[i].Escolha4 = atoi(token);
            printf("\n%d",candidatos[i].Escolha4);


            token = strtok(NULL, ",");
            strcpy(candidatos[i].curso4,token);
            printf("\n%s",candidatos[i].curso4);

            token = strtok(NULL, ",");
            candidatos[i].Escolha5 = atoi(token);
            printf("\n%d",candidatos[i].Escolha5);


            token = strtok(NULL, ",");
            strcpy(candidatos[i].curso5,token);
            printf("\n%s",candidatos[i].curso5);
            i++;

        }

        line_count++;
    }


}

I even tried using test variables ( x,y,z) to try and figure what is wrong but i cant figure it out, the program simply doesnt run the lines of coed after the function and i dont get why.

David Ranieri
  • 39,972
  • 7
  • 52
  • 94
Difrox
  • 19
  • 1
  • 3
    The code has *undefined behaviour* writing beyond memory bounds. The `ColocaCandidatos(&candidatos[60000])` is passing a pointer to an out-of-range element. Did you mean `ColocaCandidatos(candidatos)`? – Weather Vane Mar 10 '23 at 14:56
  • Also, you should definitely use `fclose` to close the file before exiting the function. – B Remmelzwaal Mar 10 '23 at 14:57
  • the file is going to be closed at the end of execution anyway, @Difrox – erik258 Mar 10 '23 at 14:58
  • OT: your positioning of the `\n` newlines is not idiomatic. Generally, they should be at the *end* of the output statement, not at the beginning. – Weather Vane Mar 10 '23 at 14:58
  • Familiarize yourself with the use of a [Debugger](https://stackoverflow.com/a/25385174/1918561) and observe your code by single-stepping through it. – Gereon Mar 10 '23 at 14:59
  • You call `strtok` many times without checking the return value for possible `NULL` pointer. You should add error detection and handling. Without that you will just happily call `strcpy` with `NULL` causing undefined behaviour. You also never verify that the token mustn't be longer than 3 characters to fit into your array of 4 bytes. – Gerhardh Mar 10 '23 at 15:52
  • 2
    Your struct fields `Escolha*` and `curso*` would better be placed as an array[5] or a struct holding one of each instances. That would allow populating them in a loop instead of writing the same code 5 times. – Gerhardh Mar 10 '23 at 15:58
  • 2
    `Candidato candidatos[60000];` This creates an array of ~3.3MB on the stack. Are you sure you have that much memory available for stack? Depending on your OS that might only be ~1MB per process. You should think about using `malloc` to allocate memory or make that one a global variable. – Gerhardh Mar 10 '23 at 16:16
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Community Mar 10 '23 at 18:52

1 Answers1

-1

I know I am two days late since this question was asked and a few people have already commented what the solution is. Here is just an answer and summary should anyone need it.

So I copied your code over to check with a debugger. I created a dummy file with the same column headers to test with.

Problem

This was identified by Weather Vane. You are trying to point to an index within the array that is not there. I noticed that on the line's where you assign the token variable to the column value. You set the parameter called *restrict __s to null.

token = strtok(NULL, ",");

Solution

When it should be pointing at the line variable, Like this.

token = strtok(line, ",");

So in short the code was reading a from a Null value and you were trying to assign these null values into an array while converting them. So the conversion would fail for Null values and Null values would end up getting assigned to the array.

This resolved the problem. I got it to print the values in the dummy file and print out the final message.

Here is the how the code should look for what you want to do.

The Code

//The Header library files that the developer was using.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//* So this is a structure that the developer needs to use for his application.
typedef struct
{
    int StudentID;                                              // The Student's ID.
    float ProvaIngresso;                                        // TryIncome? Sorry I am using google translate.
    float NotaSecundario;                                       // Secondary Note.
    float NotaCandidatura;                                      // NoteCandidacy.
    int Escolha1, Escolha2, Escolha3, Escolha4, Escolha5;       // Pick1, Pick2, Pick3, Pick4, Pick5.
    char curso1[4], curso2[4], curso3[4], curso4[4], curso5[4]; // Cursor1, Cursor2, Cursor3, Cursor4, Cursor5.
} Candidato;

//* Here he declares a function with no body.
void ColocaCandidatos(Candidato candidatos[]);

//* The main entry point of the application.
int main()
{   
    Candidato candidatos[100];                                  // Here the struct object gets declared. 60000 seems a bit excessive? 

    ColocaCandidatos(candidatos);                               // Here the method is called.

    int x = 1;
    int y = 2;
    int z = x + y;
    printf("%d", z);                                            // I think this is a basic way of checking if the code ran to the end.

    return 0;                                                   // The application ends here.
}

//* This is the method that reads the file.
void ColocaCandidatos(Candidato candidatos[])
{
    // This is where the file handling is done.
    FILE *fp;                                                   // The FILE variable is declared.
    char line[100];                                             // An array with 101 available entries.
    char *token;                                                // A token. The asterisk indicates it will be for a memory cursor variable.
    int line_count = 0;                                         // How the user keeps track of what line he is on.
    int i = 0; // token pa controlar numero do candidato;       // Translation: token to control candidate number

    // Open the CSV file for reading.
    fp = fopen("Candidatos_N10_C20_O05.csv", "r");              

    //If the FP variable is null, Then print an error saying the file could not be opened.
    if (fp == NULL)
    {
        printf("Error: could not open file\n");                 // This prints the message.
        exit(1);                                                // This exits with code 1, Meaning it will report it as an run with error's in the system logs.
    }

    // Read the lines of the file and extract the string as long as there is data in the file.
    while (fgets(line, 100, fp) != NULL)
    {
        if (line_count > 0) // Skip the first line
        { 
            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].StudentID = atoi(token);              //  This converts a string to an Integer and assigns it to Student ID variable of the Candidato structure.
            printf("\n%d", candidatos[i].StudentID);            //  This prints the Student's ID, Take note that its getting the value from the structure object. So it's a way of checking the output.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].ProvaIngresso = atof(token);          //  This converts a string to a float number and assign's it to the TryIncome variable.
            printf("\n%f", candidatos[i].ProvaIngresso);        //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].NotaSecundario = atof(token);         //  This converts a string to a float number and assign's it to the SecondaryNote variable.
            printf("\n%f", candidatos[i].NotaSecundario);       //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].NotaCandidatura = atof(token);        //  This converts a string to a float number and assign's it to the NoteCandidacy variable.
            printf("\n%f", candidatos[i].NotaCandidatura);      //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].Escolha1 = atoi(token);               //  This converts a string to a int number and assign's it to the Pick1 variable.
            printf("\n%d", candidatos[i].Escolha1);             //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            strcpy(candidatos[i].curso1, token);                //  This copies the value and assign's it to the cursor1 variable.
            printf("\n%s", candidatos[i].curso1);               //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].Escolha2 = atoi(token);               //  This converts a string to a int number and assign's it to the Pick2 variable.
            printf("\n%d", candidatos[i].Escolha2);             //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            strcpy(candidatos[i].curso2, token);                //  This converts a string to a float number and assign's it to the cursor2 variable.
            printf("\n%s", candidatos[i].curso2);               //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].Escolha3 = atoi(token);               //  This converts a string to a float number and assign's it to the Pick3 variable.
            printf("\n%d", candidatos[i].Escolha3);             //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            strcpy(candidatos[i].curso3, token);                //  This converts a string to a float number and assign's it to the cursor3 variable.
            printf("\n%s", candidatos[i].curso3);               //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].Escolha4 = atoi(token);               //  This converts a string to a float number and assign's it to the pick4 variable.
            printf("\n%d", candidatos[i].Escolha4);             //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            strcpy(candidatos[i].curso4, token);                //  This converts a string to a float number and assign's it to the cursor4 variable.
            printf("\n%s", candidatos[i].curso4);               //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            candidatos[i].Escolha5 = atoi(token);               //  This converts a string to a float number and assign's it to the Pick5 variable.
            printf("\n%d", candidatos[i].Escolha5);             //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            token = strtok(line, ",");                          //  This apparently divides strings into tokens seperated by character's.
            strcpy(candidatos[i].curso5, token);                //  This converts a string to a float number and assign's it to the cursor5 variable.
            printf("\n%s", candidatos[i].curso5);               //  This print's the variable's value from the object. It's a way of checking if a value assigned to the struct.

            i++;                                                //  Increment the loop integer.
        }

        line_count++;                                           //  Increment the line count.
    }

    fp = fclose(fp);                                            //  Close the reader, It's good practice ;).
}

Addional changes I made to the code and note's

1.I changed the parameter for ColocaCandidatos to use a normal object array.

2.I close the reader at the end of the function, Usually the file will be closed when the function ends but it's a good practice.

3.Declaring the Object Candidato with an array size of 60000 seems a bit excessive, It will be assigning atleast 3,3Mb of memory for the array every time it runs.

4.If I had more time I would have tried implementing a better way of assigning the values for the Candidato object rather than calling the same 3 line's multiple time's but to be fair not all the lines were the same. I would also add something that checks for null type value's before trying to assign the value to a variable.

  • "When it should be pointing at the line variable" That is absolutely wrong. You need to look at spec for `strtok` again. If you pass `line` for the second or any later call, you will start again and find the same token again. That is not useful. Instead you must pass `NULL` for any followup call that shall work on the same buffer that was provided earlier. – Gerhardh Mar 13 '23 at 11:28
  • @Gerhardh I tried it but I got a segmentation error and it didnt actually retrieve the value. When I changed the parameter to reference the line. Regarding 1: It was part of my troubleshooting process, It doesnt matter which one you pick. Regarding 2: Thats true, I should have said program and not function. Regarding 3: I think its pretty obvious what you should do when I say "60000 is a bit excessive". If you could provide your code on how you would do so I can see what you mean by passsing NULL. I ran this code through a debugger and it accomplished the task. – Zane Alberts Mar 13 '23 at 15:04
  • That is likely due to not checking the return value of `strtok` for `NULL` value and just put that into `strcpy` which will cause your segfault. If you pass `line` as first parameter you will get the same token again and again which is wrong. Therefore I doubt that you really checked the correct result in your debugger. Regarding 3: A beginner might not know that it makes a differences where to put that large data structure. – Gerhardh Mar 13 '23 at 15:09
  • What I mean with "passign NULL" is: If `strtok` return a `NULL` value and you do not check it, then your call `strcpy(candidatos[i].curso4, token)` will pass that `NULL` value in `token` into `strcpy` which is illegal. – Gerhardh Mar 13 '23 at 15:11
  • Look, Can I get an example of your code. I ran the code and it printed the results and telling me I dont know what I saw isnt helfpul at all, As of right now it feels like your source is just "trust me bro". Give me an example of how you would do it and I will take a look. I have given my code, You can run it through a debugger yourself. – Zane Alberts Mar 13 '23 at 15:19
  • It's not a "trust me" but a "the spec tells you" situation. BTW: The spec also tells you that `fclose` returns an `int` , not a `FILE*` which makes your `fp = fclose(fp);` incorrect. I haven't made own solution as the OP seems to ignore suggestions. But I ran your code and I get all numerical values 0 and all text values the same. – Gerhardh Mar 13 '23 at 15:40