Я пытаюсь использовать 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;
}
calories[i] += atoi(line);
— элементы массива не были обнулены, поэтому добавлять нехорошо; вы должны назначить.
спасибо @JonathanLeffler, мне пришлось инициализировать новый индекс моего массива «калории»
Вы должны показать некоторые примеры данных и ожидаемый результат. Как я отметил в своем ответе, не совсем понятно, что такое формат ввода. Если у вас есть ряд блоков, в которых есть одно число в строке с пустой строкой после блока, и вам нужно суммировать числа в блоке, чтобы создать запись в массиве, это немного отличается от того, что я показал в мой ответ. Вы ссылаетесь на «первый день появления кода 2022» — было бы разумно предоставить URL-ссылку на то, что вы спрашиваете, но еще важнее, чтобы ваш вопрос оставался понятным через десятилетие или около того.
В вашем коде подсчета калорий есть хорошие вещи, но он довольно неорганизован. Данные, выделенные 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
Время начать использовать отладчик