Ваша основная проблема заключается в том, что вы передаете указатель на grades
вашей addGrade
функции:
void addGrade(int grades[], int *numofGrades, int *max)
(int grades[]
фактически передается как int *grades
)
Когда вы делаете это, addGrade
получает копию указателя , и поскольку вы не возвращаете новый указатель, ничего, что вы делаете с grades
в addGrade
, никогда не видно вернуться в вызывающую функцию. Все изменения теряются при возврате функции.
У вас есть два (на самом деле три) варианта (1) передать ссылку на указатель grades
или (2) изменить тип возвращаемого значения от addGrade
до int *
и вернуть указатель на новый выделенный блок памяти и присвоить его grades
обратно в main()
, и (3) - заметку, вы можете передать адрес указатель для grades
на addGrade
с int**
, как в C)
Здесь, при сохранении вашего void
возвращаемого типа, разумно сделать ссылку на указатель grades
, например
void addGrade (int*& grades, int *ngrades, int gradetoadd)
(с общим примечанием, что действительно разумно использовать STL std::vector<int>
вместо основного типа int*
, но я понимаю это это учебное упражнение)
Следующее, что очень поможет при отладке, это Отделение вашей реализации от вашего пользовательского интерфейса , то есть не смешивайте пользовательский интерфейс и реальный лог обработки данных c вашего кода. Держите их отдельно. Это делает кодирование и тестирование реализацией должно быть проще. Затем добавьте интерфейс после того, как ваш основной код обработки данных будет сделан.
Например, давайте просто напишем только функцию addGrade
и короткий интерфейс, который передает ее оценки для тестирования. Единственным интерфейсом будет отправка целых чисел на addGrade
для тестирования и al oop для выдачи результатов, например,
#include <iostream>
#include <iomanip>
#include <cstring>
void addGrade (int*& grades, int *ngrades, int gradetoadd)
{
int *newgrades = new int[*ngrades + 1]; /* allocate +1 integer */
if (*ngrades) { /* if reallocating existing block of mem */
memcpy (newgrades, grades, *ngrades * sizeof *grades); /* copy */
delete[] grades; /* delete old */
}
grades = newgrades; /* assign new block of mem to pointer */
grades[(*ngrades)++] = gradetoadd; /* add grade, increment ngrades */
}
int main (void) {
int *grades = NULL, /* a pointer to block of mem holding grades */
ngrades = 0, /* number of grades stored */
tmp; /* temporary integer for input */
while (std::cin >> tmp) /* while integer read */
addGrade (grades, &ngrades, tmp); /* add to grades, passing address of ptr */
for (int i = 0; i < ngrades; i++) /* output storged grades */
std::cout << "grade[" << std::setw(2) << i << "] : " << grades[i] << '\n';
delete[] grades; /* free allocated block of memory */
}
Теперь любая отладка ограничена логами c gradeAdd
сама функция не загромождена меню, и т. д. c. Выше gradeAdd
теперь принимает ссылку на указатель для grades
, и любые изменения grades
в addGrade
теперь вносятся в исходный grades
из main()
вместо копии указателя. Компилировать и тестировать:
Пример входного файла
Файл с 20 классами 50-100:
$ cat dat/grades.txt
74
61
67
75
73
86
95
54
93
99
68
100
95
84
50
58
79
86
98
80
Пример использования / вывода
Просто перенаправьте файл dat/grades.txt
в качестве ввода на stdin
в вашу тестовую программу:
$ ./bin/addgrade < dat/grades.txt
grade[ 0] : 74
grade[ 1] : 61
grade[ 2] : 67
grade[ 3] : 75
grade[ 4] : 73
grade[ 5] : 86
grade[ 6] : 95
grade[ 7] : 54
grade[ 8] : 93
grade[ 9] : 99
grade[10] : 68
grade[11] : 100
grade[12] : 95
grade[13] : 84
grade[14] : 50
grade[15] : 58
grade[16] : 79
grade[17] : 86
grade[18] : 98
grade[19] : 80
Использование памяти / проверка ошибок
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанности в отношении любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памяти, так, (2) он может быть освобожден , когда он больше не нужен.
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
это нормальный выбор. Есть похожие проверки памяти для каждой платформы. Все они просты в использовании, просто запустите вашу программу через нее.
$ valgrind ./bin/addgrade < dat/grades.txt
==4612== Memcheck, a memory error detector
==4612== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4612== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==4612== Command: ./bin/addgrade
==4612==
grade[ 0] : 74
grade[ 1] : 61
grade[ 2] : 67
grade[ 3] : 75
grade[ 4] : 73
grade[ 5] : 86
grade[ 6] : 95
grade[ 7] : 54
grade[ 8] : 93
grade[ 9] : 99
grade[10] : 68
grade[11] : 100
grade[12] : 95
grade[13] : 84
grade[14] : 50
grade[15] : 58
grade[16] : 79
grade[17] : 86
grade[18] : 98
grade[19] : 80
==4612==
==4612== HEAP SUMMARY:
==4612== in use at exit: 0 bytes in 0 blocks
==4612== total heap usage: 23 allocs, 23 frees, 78,664 bytes allocated
==4612==
==4612== All heap blocks were freed -- no leaks are possible
==4612==
==4612== For counts of detected and suppressed errors, rerun with: -v
==4612== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что в ней нет ошибок памяти. (Вся свободная память и отсутствие ошибок является подтверждением того, что вы используете память, которую вы правильно распределили)
Теперь вы прочитали функцию gradeAdd
, чтобы включить ее в оставшуюся часть кода, и вы можете быть уверены, что там нет никаких проблем с этой функцией. Любая дальнейшая отладка упрощается. Посмотрите вещи и дайте мне знать, если у вас есть вопросы.