Realloc() значение изменения языка C в массиве int

Я пытаюсь использовать realloc() каждый цикл, поэтому я использую только необходимую память для моего массива int в C, но выходные значения меняются. Тем не менее, при использовании Valgrind в моем коде у меня есть правильные значения.

Делаю первый день Пришествия Кода 2022.

Входной файл представляет собой файл .txt, который выглядит следующим образом:

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

Я пытался суммировать числа и хранить их в своем массиве в определенном индексе, и если есть пустая строка, увеличьте мой индекс.

Учитывая этот пример ввода, он должен быть напечатан следующим образом:

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

Что я получил:

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

Что я хочу (результат с использованием valgrind):

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

Мой код:

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;
}

Время начать использовать отладчик

Jabberwocky 06.12.2022 19:11
calories[i] += atoi(line); — элементы массива не были обнулены, поэтому добавлять нехорошо; вы должны назначить.
Jonathan Leffler 06.12.2022 19:32

спасибо @JonathanLeffler, мне пришлось инициализировать новый индекс моего массива «калории»

aisezc 06.12.2022 19:45

Вы должны показать некоторые примеры данных и ожидаемый результат. Как я отметил в своем ответе, не совсем понятно, что такое формат ввода. Если у вас есть ряд блоков, в которых есть одно число в строке с пустой строкой после блока, и вам нужно суммировать числа в блоке, чтобы создать запись в массиве, это немного отличается от того, что я показал в мой ответ. Вы ссылаетесь на «первый день появления кода 2022» — было бы разумно предоставить URL-ссылку на то, что вы спрашиваете, но еще важнее, чтобы ваш вопрос оставался понятным через десятилетие или около того.

Jonathan Leffler 06.12.2022 19:53
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
4
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашем коде подсчета калорий есть хорошие вещи, но он довольно неорганизован. Данные, выделенные malloc(), не обнуляются, поэтому использовать += в calories[i] += atoi(line); нехорошо. Вы не показали формат входных данных.

Неясно, нужно ли вам читать кучу чисел до пустой строки и сохранять сумму в массиве (и промывать и повторять в EOF), или вам просто нужно читать числа из файла и сохранять их в массив.

Каждая строка имеет номер, который нужно хранить отдельно

В приведенном ниже коде предполагается, что каждая строка содержит число, которое следует сохранить в массиве. Приспособиться к другому стилю обработки несложно.

#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;
}

Я не в восторге от perror() — он работает и прост, но получить от него хорошие сообщения относительно сложно. Код гарантирует наличие в массиве дополнительной записи для нулевой записи в конце. Однако он не обнаруживает нулевую запись в середине массива. Обычно это происходит из-за того, что atoi() не удалось преобразовать значение.

Я сгенерировал файл input.txt, содержащий 10 случайных значений от 100 до 1000:

478
459
499
997
237
423
185
630
964
594

Вывод из программы был:

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 

Суммируемые блоки чисел, разделенные пустыми строками

Этот код тесно связан с предыдущим ответом, но код «добавить в массив» извлекается в функцию, поэтому его можно использовать дважды. Возможно, было бы лучше использовать структуру для инкапсуляции деталей массива. Я, вероятно, также должен использовать size_t, а не int для размеров.

#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;
}

Пересмотренный файл данных:

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

(Обратите внимание, что в конце нет пустой строки!)

Вывод:

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 для перекрестной проверки результата:

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

Результаты:

2855
4884
2251
3528
2853
4968
1810
932
4017
1180

Другие вопросы по теме