Вы делаете вещи намного сложнее, чем они должны быть. Вы динамически распределяете память для codedPtr
и decodedPtr
, нет необходимости делать два прохода по файлу (один для подсчета целых чисел и один для чтения после выделения). Ваш decode
намного сложнее, чем необходимо, и есть ошибка logi c. Добавление '0'
(в этом случае нет необходимости - хотя обычно требуется преобразовать десятичное значение di git в его символьное значение ASCII)
Для адреса load_nums
измените тип возврата - int *
и выделить для codedPtr
в пределах load_nums
, используя realloc
по мере необходимости, чтобы увеличить размер выделенного блока памяти. Затем верните указатель на выделенный блок памяти, содержащий ваши значения int
. Передайте ncount
в качестве указателя (например, int *ncount
), чтобы вы могли обновить значение по этому адресу числом прочитанных целых чисел, чтобы счет был доступен обратно в вызывающей функции (main()
здесь).
При таком подходе к распределению файлов ваш ввод-вывод сокращается до однопроходного прохода через файл (а файловый ввод-вывод является одной из самых трудоемких операций). Кроме того, вы полностью исключаете необходимость num_count()
function.
Соединяя эти части вместе, вы можете сделать:
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
Вы бы вызвали функцию в main()
as, codedPtr = load_nums (fp, &numCount)
. Вы можете заключить его в оператор if(...)
, чтобы определить, успешно ли выполнено выделение и чтение:
int *codedPtr = NULL, numCount = 0;
...
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
(нет необходимости передавать codedPtr
из main()
. Дальнейшую проверку можно выполнить с помощью проверка numCount > 0
- это вам осталось)
Для вашей функции decode
просто установите for
l oop, используйте две переменные l oop для итерации с начала и до конца к середине. Это значительно упрощает ситуацию, например,
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
(i
начинается с первого целого числа и j
- до последнего. Не используйте *(codePtr + i)
вместо использования codePtr[i]
- хотя и эквивалентно, индексную нотацию легче читать)
В main()
вы можете альтернативно открыть файл, предоставленный в качестве первого аргумента вашей программе, или прочитать из stdin
по умолчанию, если аргумент не предоставлен (так много Linux утилиты работают). Добавление простого троичного - это все, что вам нужно. Читаете ли вы ввод или выделяете память (или используете какую-либо функцию, необходимую для продолжения правильной работы вашего кода), вы не сможете использовать эту функцию правильно, если вы не проверили возврат в определить, была ли операция успешной или неудачной. Урок: валидация, валидация, валидация ... .
В целом, вы можете сделать:
#include <stdio.h>
#include <stdlib.h>
/* read integers from fp, dynamically allocating storage as needed,
* return pointer to allocated block holding integers and make ncount
* available through update pointer value.
*/
int *load_nums (FILE* fp, int *ncount)
{
int *codedPtr, avail = 2; /* declare pointer & no. to track allocated ints */
*ncount = 0; /* zero the value at ncount */
/* allocate avail no. of int to codedPtr - validate EVERY allocation */
if (!(codedPtr = malloc (avail * sizeof *codedPtr))) {
perror ("malloc-codedPtr");
return NULL;
}
while (fscanf (fp, "%d", &codedPtr[*ncount]) == 1) { /* read each int */
if (++(*ncount) == avail) { /* check if realloc needed (count == avail) */
/* always realloc to a temporary pointer */
void *tmp = realloc (codedPtr, 2 * avail * sizeof *codedPtr);
if (!tmp) { /* validate that realloc succeeds */
perror ("realloc-codedPtr");
return codedPtr; /* original codedPtr vals available on failure */
}
codedPtr = tmp; /* assign new block of mem to codedPtr */
avail *= 2; /* update avail with no. of int allocated */
}
}
return codedPtr; /* return pointer to allocated block of memory */
}
void decode (int *codedPtr, int ncount, char *decodedPtr)
{
/* loop from ends to middle adding values, + '0' NOT required */
for (int i = 0, j = ncount - i - 1; i < j; i++, j--)
decodedPtr[i] = codedPtr[i] + codedPtr[j];
}
int main(int argc, char *argv[]) {
int *codedPtr = NULL, numCount = 0;
char *decodedPtr = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!(codedPtr = load_nums (fp, &numCount))) /* read file/validate */
return 1;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!(decodedPtr = malloc (numCount + 1))) { /* allocate/validate */
perror ("malloc-decodedPtr"); /* don't forget room for '\0' */
return 1;
}
decode (codedPtr, numCount, decodedPtr); /* decode the message */
decodedPtr[numCount] = 0; /* nul-terminate */
puts (decodedPtr); /* output decoded message */
free (codedPtr); /* don't forge to free what you allocate */
free (decodedPtr);
}
Пример использования / Вывод
Тестируя вашу программу, вы обнаруживаете, что декодированное сообщение равно "Hello"
, например,
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | ./bin/codedptr
Hello
Проверка использования памяти / ошибок
В любом коде вы пишете, что динамически распределяете память, у вас есть 2 обязанностей относительно любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памяти, поэтому, (2 ) он может быть освобожден , когда он больше не нужен.
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за ее пределами. границы выделенного блока, попытайтесь прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
это нормальный выбор. Есть похожие проверки памяти для каждой платформы. Все они просты в использовании, просто запустите вашу программу через нее.
$ echo "-6 -76 53 -34 32 79 142 55 177 78" | valgrind ./bin/codedptr
==32184== Memcheck, a memory error detector
==32184== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==32184== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==32184== Command: ./bin/codedptr
==32184==
Hello
==32184==
==32184== HEAP SUMMARY:
==32184== in use at exit: 0 bytes in 0 blocks
==32184== total heap usage: 7 allocs, 7 frees, 5,251 bytes allocated
==32184==
==32184== All heap blocks were freed -- no leaks are possible
==32184==
==32184== For counts of detected and suppressed errors, rerun with: -v
==32184== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.