1

I have written some C-code and I'm getting compiler warnings when I try to free previously allocated data.

The warnings (in Visual Studio 2022) are:

Warning C6001 Using uninitialized memory '*experiments.mass'.
Warning C6001 Using uninitialized memory '*experiments.length'.

The program is reading from txt file named, "result," and checks the measurements to calculate spring constant.

The warning is on the lines:

    free(experiments[i].mass); //free all the data that saved by the order of the saving
    free(experiments[i].length);

Here is a compilable code section that reproduces the diagnostics:

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


#define GRAVITY 9.81

typedef struct {
    double* mass;
    double* length;
    int exp_num;
    int numMeasurements;
    double k;
    double r2;
} Exp_t;

void errorList(int error_num);

/*the function get the file name, open the file and read ho many times the word experiment is shown
in the end the function return int num that count how many times the word is in the file*/
int counting_experiment(const char* file_name);

/*the same as counting experiment but check what is the maximum measurements for each experiment*/
int counting_measurements(const char* file_name);

/*read string that the user input and give back int number that the string contains.
if the string have char that is not number it brings back error.*/
int StringToInt(const char* str);

/*the function get the Exp_t array and the number of experiments and sorting the exp_num from the
smallest to the biggest*/
void sortExperiments(Exp_t* experiments, int numExperiments);

/*the function takes the file name and the Exp_t array, and experiment and measurements counters 
the function go over the file and put in the structure all of the experiments data.*/
void Read_File(const char* file_name, Exp_t* experiments, int* numExperiments, int max_measuerment);

/*this function get char from the user and check if he want to continue or not.
if the char is illegal it gives an error massage if it is legal char it make the relevant commands. */
char prog_continue(char user_input);

/*the function calculate the spring constant and get the specific struct of Exp_t
that relevant to the user input experiment number. the function output is print of the calculated value
and saving k to the correct struct in order to be able use it later*/
void calculateSpringConstant(Exp_t* experiment, int max_measurement);

/*get the structure array and print all the information that relevant 
to the [i] place in the structure*/
void Print_File(const char* file_name, const Exp_t* experiments, int numExperiments);

int main() {
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    const char* file_name = "Results.txt";
    const char* Proc_File_Name = "processed_206523649.txt";
    Exp_t* experiments;
    int numExperiments = 0;
    int counting_exp = counting_experiment(file_name);
    experiments = (Exp_t*)calloc(counting_exp, sizeof(Exp_t));
    if (experiments == NULL) exit(1); // check if the calloc worked.
    int counting_measurement = counting_measurements(file_name);

    Read_File(file_name, experiments, &numExperiments, counting_measurement);

    char selected_exp_str[50];
    int selected_exp;
    char to_continue = '\0';
    do
    {
        printf_s("What experiment do you want to analyze: \n");
        scanf_s("%s", selected_exp_str, (unsigned int)sizeof(selected_exp_str) - 1);
        selected_exp = StringToInt(selected_exp_str); // this part check if the input is a positive int
        if (selected_exp < 0)
        {
            errorList(1);
            while (getchar() != '\n');
            continue;
        }

        int exp_index = -1;
        for (int i = 0; i < numExperiments; i++) //the loop check for match between user input to exp_num
        {
            if (experiments[i].exp_num == selected_exp) //check if there is match between the user input to the available exp
                exp_index = i; // if there is sa match change the flag
        }
        if (exp_index == -1) { // if the flag hasn't changed print error.
            printf_s("Experiment %s does not exist in Result.txt. Please select one of the following experiments:\n", selected_exp_str);
            for (int i = 0; i < numExperiments; i++)
                printf_s("%d\n", experiments[i].exp_num);
        }
        else
            calculateSpringConstant(&experiments[exp_index], counting_measurement); //calculate the k and r2 for the selected exp
    } while (prog_continue(to_continue) != 'n'); //running until the user input for ending is 'n'

    sortExperiments(experiments, numExperiments); // Sort experiments array before printing
    Print_File(Proc_File_Name, experiments,numExperiments);
    printf_s("Experiment data saved to processed_206523649.txt.\n");

    for (int i = 0; i < counting_exp; i++)
    {
        free(experiments[i].mass); //free all the data that saved by the order of the saving
        free(experiments[i].length);
    }

    free(experiments);
    return 0;
}
int counting_experiment(const char* file_name) {
    FILE* file;
    fopen_s(&file, file_name, "rt");
    if (!file) exit(1);
    char line[100];
    int current_exp = -1;
    int exp_counting = 0;
    while (fgets(line, sizeof(line), file)) {//the loop go over each raw and check if experiment word is in it. if yes it add 1 to the counter
        if (sscanf_s(line, "experiment_%d", &current_exp) == 1)
            exp_counting++;
    }
    return exp_counting;
}

int counting_measurements(const char* file_name){
    FILE* file;
    fopen_s(&file, file_name, "rt");
    if (!file) exit(1);
    char line[100];
    int counter = 0, temp_counter = 0, current_exp = -1;
    while (fgets(line, sizeof(line), file)) {
        if (line[0] == 'm') {
            temp_counter++; //running on all the measurement line till End and plus 1 to the temp counter on each line
            continue; 
        }
        if (temp_counter > counter) {
            counter = temp_counter; // if the temp is bigger update the counter. in the end counter will be the biggest
            temp_counter = 0;
        }
    }
    return counter;
}

void Read_File(const char* file_name, Exp_t* experiments, int* numExperiments, int max_measurement) {
    FILE* file;
    fopen_s(&file, file_name, "rt");
    if (!file) exit(1); // open the file to read only and crush if it not find the file

    int current_exp = -1;
    char line[100];
    while (fgets(line, sizeof(line), file)) { //loop running on each line until EOF
        if (sscanf_s(line, "experiment_%d", &current_exp) == 1) //if experiment found set the value for the structure
        {
            experiments[*numExperiments].exp_num = current_exp;
            experiments[*numExperiments].mass = (double*)calloc(max_measurement, sizeof(double));
            experiments[*numExperiments].length = (double*)calloc(max_measurement, sizeof(double));
            if (experiments[*numExperiments].mass == NULL || experiments[*numExperiments].length == NULL ){
                exit(1); //save memory for the max measurements that could be and check if it worked
            }
            experiments[*numExperiments].k = 0.0; 
            experiments[*numExperiments].r2 = 0.0; //initialize k and r2 for each exp to 0
            experiments[*numExperiments].numMeasurements = 0;
            (*numExperiments)++;
        }
        if (line[0] == 'm')
        {
            double mass, length; //if line is start with m it saves the data for mass and length to the structure
            if (sscanf_s(line, "m:%lf l:%lf", &mass, &length) == 2)
            {
                experiments[*numExperiments - 1].mass[experiments[*numExperiments - 1].numMeasurements] = mass;
                experiments[*numExperiments - 1].length[experiments[*numExperiments - 1].numMeasurements] = length;
                experiments[*numExperiments - 1].numMeasurements++;
            }
        }
        else if (strcmp(line, "end\n") == 0) { //if the line is end initialize the counter
            current_exp = -1;
        }
    }
    fclose(file);
}

I expected to get all the data free and it did work when the code is like this.

However, I don't want the warnings but, on the other hand, when I free the data without loop in this way:

free(experiments->mass);
free(experiments->length);
free(experiment);

then the compiler doesn't show any error or warning but the _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); shows a memory leak.

Allan Wind
  • 23,068
  • 5
  • 28
  • 38
tomer
  • 29
  • 2
  • 1
    From a first glance at the code, you read data into `numExperiments` elements of your array. But you iterate over `counting_exp` elements for freeing. How do you ensure that the latter is not larger then the first? – Gerhardh Jun 02 '23 at 11:21
  • hi, counting_exp is used in the begining to check how many experiments there is in order to set up the calloc for experient. it is the same like numExperiments becuse it make the same reading from the file. but i triead also change the loop to numExperiments and it didnt make a change – tomer Jun 02 '23 at 11:24
  • @Gerhard From what I can see, that shouldn't matter: The `experiments` array is allocated with `calloc`, which will initialize all bytes to zero ... and calling `free` on a `NULL` pointer is a well-defined NOP. – Adrian Mole Jun 02 '23 at 11:25
  • 1
    Maybe the compiler doesn't realize that. If it is the same, you could use the same variable in the freeing loop. – Gerhardh Jun 02 '23 at 11:26
  • @AdrianMole I know. My idea is that the loop might try to free more elements than what was allocated (and initialized). – Gerhardh Jun 02 '23 at 11:27
  • The allocation call uses the same counter as the `free` loop. Furthermore, even making the declaration into a `const` (i.e. `const int counting_exp = counting_experiment(file_name);`) doesn't remove the warnings. Looks like a M/S false positive. – Adrian Mole Jun 02 '23 at 11:29
  • @Gerhardh it didnt help to make the change, maybe you see another thing? – tomer Jun 02 '23 at 11:29
  • OK, then this is not the problem. Got it. – Gerhardh Jun 02 '23 at 11:30
  • Is the dynamically allocated arrays `mass` and `length` in `Exp_t` always of the same length? – Ted Lyngmo Jun 02 '23 at 11:35
  • @AdrianMole what is the meaning in M/S false positive? and there is somthing i can do with it? – tomer Jun 02 '23 at 11:37
  • @TedLyngmo yes, it always allocated by the maximum measurment that experiment can have in the original file (that what i check in the start with 'conting_measuerments'). i cant use DEFINE because i dont know the value before reading the file – tomer Jun 02 '23 at 11:39
  • 1
    Ok, then define a type that stores _one_ pair of `mass` and `length` and dynamically allocate an array of that type. [example](https://godbolt.org/z/hMGb8nn3Y) – Ted Lyngmo Jun 02 '23 at 11:42
  • Please [edit] and show the __verbatim__ error log. – Jabberwocky Jun 02 '23 at 11:47
  • A "false positive" (in this case from the MSVC static analyser) is when it's giving you what it *thinks* is a problem in your code ... but actually isn't). The C6001 is one of those that MSVC gives a lot. There are often some "quick tricks" you can add to your code (like in [this recent answer I posted](https://stackoverflow.com/a/76302107/10871073)) but, in that case, my fix was to use `calloc` instead of `malloc` ... which you're already doing. ... – Adrian Mole Jun 02 '23 at 11:48
  • ... I like this question because it's challenging me and making me scratch my head (even more than the nassssty mosquitos). Have an upvote, in the meantime, while I try to figure out how to convince the compiler that your code is OK. :-) – Adrian Mole Jun 02 '23 at 11:49
  • 1
    @Jabberwocky I just copy/pasted the OP's code into my VS IDE and, when I run Code Analysis, I get the exact warning messages that the OP has **already** posted. (OK, I tidied up the post formatting a bit, but it's a new user...) – Adrian Mole Jun 02 '23 at 11:56
  • @AdrianMole thank you so much for helping. i also keep trying tricks from the web to fix that warning. hope that we will sucseed! – tomer Jun 02 '23 at 12:01
  • On issue I can see in your code is that you're not handling the case when the `int counting_exp = counting_experiment(file_name);` gives a zero result for the count. However, I added just such a check and it didn't silence the warnings. – Adrian Mole Jun 03 '23 at 05:53

0 Answers0