Ошибка при освобождении памяти. HEAP[ConsoleApplication1.exe]: Invalid address specified to
Есть код, Который подсчитывает и выводит слова, игнорируя сепараторы. Почему-то при освобождении памяти происходит ошибка.
C:
// Библиотеки
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef struct Tokens_s {
int num;
char **arr;
} Tokens;
// Объявления функций
char* GetString();
bool CheckSeparator(char letter, const char *delims);
void tokensSplit(Tokens *tokens, const char *str, const char *delims);
void PrintMe(Tokens *tokens);
void tokensFree(Tokens *tokens);
// Основной цикл
int main() {
// Создаем структуру (динамически)
Tokens* token = (Tokens*)malloc(sizeof(Tokens));
// Инициализируем структуру
token->num = 0;
token->arr = (char**)malloc(sizeof(char*));
// Инициируем массив разделителей
const char separators[] = {'.', ',',':',';', 0};
// Ввод строки
char* inputStr = GetString();
// Обработка строки
tokensSplit(token, inputStr, separators);
// Освобождение памяти строки
if (inputStr)
free(inputStr);
// Вывод
PrintMe(token);
// Освобождение памяти токена
tokensFree(token);
return 0;
}
// Получение строки
char* GetString() {
// Инициализация строки
int position = 0;
int buffer = 100;
const double rate = 1.25;
char* string = (char*)malloc(buffer*sizeof(char));
// Считываение строки
char letter = getchar();
// && letter != '#' FOR WINDOWS ONLY
// EOF - -1
// '\0' - 0
while (letter != EOF && letter != '#') {
string[position] = letter;
buffer--;
position++;
if (!buffer) {
buffer = position * rate;
// Здесь можно добавить ограничение на 10^6
string = (char*)realloc(string, (position+buffer)*sizeof (char));
}
letter = getchar();
}
// Корректируем размер строки и записываем СТОП-символ
string = (char*)realloc(string, (position+1)*sizeof (char));
string[position] = 0; // '\0'
// Возвращаем ответ
return string;
}
// Проверка на сепаратор
bool CheckSeparator(char letter, const char *delims) {
if (!delims) {
printf ("%s", "NullException");
return false;
}
for (int idx = 0; delims[idx] != 0; idx++) {
if (delims[idx] == letter)
return true;
}
return false;
}
// Обработка строки
void tokensSplit(Tokens *tokens, const char *str, const char *delims) {
// Проверки на пустоту
if (!tokens || !str|| !delims) {
printf ("%s", "NullException");
return;
}
// Пропускаем первые сепараторы
int idx = 0;
while (str[idx] != 0 && CheckSeparator(str[idx], delims))
idx++;
// Обход строки
while (str[idx] != 0) {
// Проверка на сепаратор
bool IsSeparator = CheckSeparator(str[idx], delims);
// Если это буква
if (!IsSeparator) {
// Вычисляем размер слова
// Т.к. в текущий символ проверили - пропускаем
int wordSize = 1;
while (str[idx+wordSize] != 0 && !CheckSeparator(str[idx+wordSize], delims))
wordSize++;
// Выделяем память
tokens->arr[tokens->num] = (char*)malloc((wordSize+1)*sizeof(char));
// Копируем слово
memcpy(tokens->arr[tokens->num], (str+idx), wordSize*sizeof(char));
tokens->arr[tokens->num][wordSize] = 0;
tokens->num++;
// Перемещаем индекс
idx += wordSize;
} else
// Иначе переходим к следующему символу
idx++;
}
}
// Печать такенов
void PrintMe(Tokens *tokens) {
// Проверки на пустоту
if (!tokens) {
printf ("%s", "NullException");
return;
}
// Выводим кол-во слов
printf("%i\n", tokens->num);
// Выводим слова
for (int i = 0; i < tokens->num; i++)
printf("%s\n", tokens->arr[i]);
}
void tokensFree(Tokens *tokens) {
// Проверки на пустоту
if (!tokens)
return;
// Удаление строк
for (int idx = 0; idx < tokens->num; idx++) {
if (tokens->arr[idx])
free(tokens->arr[idx]);
}
// Удаление массива строк
if (tokens->arr)
free(tokens->arr);
// Удаление токена
if (tokens)
free(tokens);
}
Вводим следующие:
..ko,.Privet:kreved,.,:ko:;,,.#
Вылетает ошибка:
HEAP[ConsoleApplication1.exe]: Invalid address specified to RtlValidateHeap( 00EA0000, 00EADDD8 )
Место ошибки четко не выдает, но где-то здесь судя по отладке:
// Удаление строк
for (int idx = 0; idx < tokens->num; idx++) {
if (tokens->arr[idx])
free(tokens->arr[idx]);
}
// Удаление массива строк
if (tokens->arr)
free(tokens->arr);
Ответы (1 шт):
Ваша проблема вот в этой строке
// Выделяем память
tokens->arr[tokens->num] = (char*)malloc((wordSize+1)*sizeof(char));
а чуточку ниже есть
tokens->num++;
то есть, массив arr должен содержать достаточно памяти, что бы вместить массив. Но что там?
token->num = 0;
token->arr = (char**)malloc(sizeof(char*));
а там памяти на один элемент. Что ж. попробуем закостылить
token->num = 0;
token->arr = (char**)malloc(sizeof(char*)*100);
ура, ничего не падает! Ну пока в строке не будет больше 100 "слов". Ок, теперь пофиксим чуточку корректнее.
найдем строку (это где то 130-140 строка кода)
tokens->num++;
и допишем там такое
tokens->arr = realloc(tokens->arr, (tokens->num+1)* sizeof(char*));
Да, у нас получается, что всегда выделено на один элемент больше, но на то оно и быстрый костыль.
Для хорошей переделки нужно более корректно выделять память, но это уже отдельная тема.
кстати, писать вот так
arr = realloc(arr, ...)
считается не очень хорошо. если вдруг realloc не сможет выделить память, то будет утечка. Но очень часто либо игнорируют это (в домашних заданиях), либо делают что то такое
tmp = realloc(arr,...);
if (tmp == NULL) {
// что то плохое случилось. можно выйти, а можно попобовать работать дальше
}
else {
arr = tmp;
// все хорошо, работаем
}