Home > front end >  realloc() C-language change value in int array
realloc() C-language change value in int array

Time:12-07

I'm trying to use realloc() every loop, so I only use necessary memory for my int array in C, but the output values are changed. Nevertheless, when using Valgrind on my code, I have the right values.

I'm doing the first day of Advent of Code 2022.

The input file is a .txt file that looks like this:

7569
1357
10134
4696
4423
8869
3562
6597

4038
9038
1352
8005
4811
6281
3961
4023

7234
3510
7728
1569
4583
7495
3941
6015
6531
2637

I was trying to sum numbers and store it in my array in a specific index, and if there is a blank line increase my index.

Given that example input, it should print like this:

elf [0] = 47207
elf [1] = 41509 
elf [2] = 51243

What I got:

elf [245] = 63138
elf [246] = 181168
elf [247] = 41570
elf [248] = 36264
elf [249] = 59089
elf [250] = 185061

What I want (result using valgrind):

elf [245] = 63138
elf [246] = 52399
elf [247] = 41570
elf [248] = 36264
elf [249] = 59089
elf [250] = 56308

My code:

int *read_calories(char *filename)
{
    FILE *fp = fopen(filename, "r");
    char *line = NULL;
    int i = 0;
    size_t len = 0;
    ssize_t nread;
    struct stat size;
    stat(filename, &size);
    int tab_size = 1;
    int *calories = malloc(sizeof(int) * 2);

    if (fp == NULL)
    {
        perror("Can't open file\n");
        exit(EXIT_FAILURE);
    }

    while ((nread = getline(&line, &len, fp)) != -1) 
    {
        if (nread == 1) {
            i  ;
              tab_size;
            calories = realloc(calories, tab_size * sizeof(int));
        } else {
            calories[i]  = atoi(line);
        }
    }
    calories[i   1] = '\0';
    free(line);
    fclose(fp);

    return calories;
}

int main()
{
    int *calories = read_calories("input.txt");
    for (int i = 0; calories[i] != '\0'; i  ) {
        printf("elf [%d] = %d \n", i, calories[i]);
    }
    free(calories);
    return 0;
}

CodePudding user response:

Your calorie reading code has good stuff in it, but is rather disorganized. The data allocated by malloc() is not zeroed, so using = in calories[i] = atoi(line); is not good. You've not shown the input data format.

It isn't clear whether you have to read a bunch of numbers up to a blank line and store the sum in the array (and rinse and repeat to EOF), or whether you just need to read numbers from the file and store those into the array.

Each line has a number to be stored separately

The code below assumes that each line contains a number that should be stored in the array. Adapting to the other style of processing is not difficult.

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

extern int *read_calories(const char *filename);

int *read_calories(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp == NULL)
    {
        fprintf(stderr, "Failed to open file '%s' for reading (%d: %s)\n", filename, errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    int tab_used = 0;
    int tab_size = 2;
    int *calories = malloc(sizeof(int) * tab_size);
    if (calories == NULL)
    {
        fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    char *line = NULL;
    size_t len = 0;
    while (getline(&line, &len, fp) != -1) 
    {
        if (tab_used == tab_size - 1)
        {
            size_t new_size = 2 * tab_size;
            void  *new_data = realloc(calories, new_size * sizeof(int));
            if (new_data == NULL)
            {
                fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
                exit(EXIT_FAILURE);
            }
            calories = new_data;
            tab_size = new_size;
        }
        calories[tab_used  ] = atoi(line);
    }
    calories[tab_used] = 0;
    free(line);
    fclose(fp);

    return calories;
}

int main(void)
{
    int *calories = read_calories("input.txt");
    assert(calories != NULL);
    for (int i = 0; calories[i] != 0; i  )
        printf("elf [%d] = %d \n", i, calories[i]);
    free(calories);
    return 0;
}

I'm not keen on perror() — it does a job and is simple, but it's relatively hard to get good messages out of it. The code ensures there's an extra entry in the array for a zero entry at the end. It does not, however, spot a zero entry in the middle of the array. That would usually be caused by atoi() failing to convert the value.

I generated an input.txt file containing 10 random values between 100 and 1000:

478
459
499
997
237
423
185
630
964
594

The output from the program was:

elf [0] = 478 
elf [1] = 459 
elf [2] = 499 
elf [3] = 997 
elf [4] = 237 
elf [5] = 423 
elf [6] = 185 
elf [7] = 630 
elf [8] = 964 
elf [9] = 594 

Blocks of numbers to be summed, separated by blank lines

This code is closely based on the previous answer, but the 'add to the array' code is extracted into a function so it can be used twice. It might be better to use a structure to encapsulate the array details. I should probably also use size_t rather than int for the sizes.

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

static void add_to_array(int **table, int *tab_size, int *tab_used, int value)
{
    if (*tab_used == *tab_size - 1)
    {
        size_t new_size = 2 * *tab_size;
        void  *new_data = realloc(*table, new_size * sizeof(int));
        if (new_data == NULL)
        {
            fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
            exit(EXIT_FAILURE);
        }
        *table = new_data;
        *tab_size = new_size;
    }
    (*table)[(*tab_used)  ] = value;
}

static int *read_calories(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp == NULL)
    {
        fprintf(stderr, "Failed to open file '%s' for reading (%d: %s)\n", filename, errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    int tab_used = 0;
    int tab_size = 2;
    int *calories = malloc(sizeof(int) * tab_size);
    if (calories == NULL)
    {
        fprintf(stderr, "Failed to allocate memory (%d: %s)\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    char *line = NULL;
    size_t len = 0;
    int current_sum = 0;
    ssize_t nread;
    while ((nread = getline(&line, &len, fp)) != -1) 
    {
        if (nread == 1)
        {
            add_to_array(&calories, &tab_size, &tab_used, current_sum);
            current_sum = 0;
        }
        else
            current_sum  = atoi(line);
    }
    if (current_sum > 0)
        add_to_array(&calories, &tab_size, &tab_used, current_sum);
    calories[tab_used] = 0;
    free(line);
    fclose(fp);

    return calories;
}

int main(void)
{
    int *calories = read_calories("input.txt");
    assert(calories != NULL);
    for (int i = 0; calories[i] != 0; i  )
        printf("elf [%d] = %d \n", i, calories[i]);
    free(calories);
    return 0;
}

Revised data file:

184
861
513
507
790

897
715
287
729
534
777
945

950
696
605

287
763
839
860
779

522
140
281
190
744
976

420
462
591
710
435
707
580
855
208

806
205
799

537
395

922
356
397
464
435
470
973

203
713
264

(Note that there isn't a blank line at the end!)

Output:

elf [0] = 2855 
elf [1] = 4884 
elf [2] = 2251 
elf [3] = 3528 
elf [4] = 2853 
elf [5] = 4968 
elf [6] = 1810 
elf [7] = 932 
elf [8] = 4017 
elf [9] = 1180 

Awk script to cross-check the result:

awk 'NF == 0 { print sum; sum = 0 } NF == 1 { sum  = $1 } END { print sum }' input.txt

Results:

2855
4884
2251
3528
2853
4968
1810
932
4017
1180
  • Related