Динамическое расширение массива указателей

Я пытаюсь создать словарь с помощью С++. Словарь должен постоянно создаваться и обновляться динамически. Например, скажем, у меня есть 5 слов в моем словаре, и я хочу добавить еще одно слово, я должен создать новый словарь с местом для 6 слов, скопировать старые слова и добавить новое слово в новый словарь.

В моей функции main я создал lexicon** (указатель на массив указателей, поскольку каждое слово имеет указатель char на него). Я создал функцию newStr, чтобы получить новое слово, добавить его в словарь и отсортировать по алфавиту.

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

0xC0000005: место чтения нарушения прав доступа 0xDDDDDDDD.

Я не понимаю, что я делаю неправильно. Спасибо за помощь!

Вот мой код:

#define MAX 80
#include <iostream>
#include <cstring>
#include <string.h>
using namespace std;

void newStr(char** lexicon, int& lexiconSize, char word[])
{
    // create the new updated lexicon
    char** updated = new char*[++lexiconSize];

    // copy the words from the old to the updated lexicon
    for (int i = 0; i < lexiconSize; i++)
    {
        updated[i] = new char[MAX];

        if (i < lexiconSize - 1)
        {
            strcpy_s(updated[i], MAX, lexicon[i]);
        }

        // add the new word to the end of the updatedLexicon
        else
        {
            strcpy_s(updated[i], MAX, word);
        }
    }

    // deallocate the memory of the worlds of the old lexicon
    for (int i = 0; i < lexiconSize - 1; i++)
    {
        delete[] lexicon[i];
    }

    // deallocate the memory of the old lexicon
    delete[] lexicon;

    // point the lexicon pointer to the updatedLexicon
    lexicon = updated;

    // now sort the lexicon including the new word
    if (lexiconSize > 1)
    {
        for (int i = 1; i < lexiconSize; i++)
        {
            for (int j = 1; j < lexiconSize; j++)
            {
                if (strcmp(lexicon[j - 1], lexicon[j]) > 0)
                {
                    char t[MAX];
                    strcpy_s(t, MAX, lexicon[j - 1]);
                    strcpy_s(lexicon[j - 1], MAX, lexicon[j]);
                    strcpy_s(lexicon[j], MAX, t);
                }
            }
        }
    }

    // deallocate the memory created for the updated lexicon
    for (int i = 0; i < lexiconSize; i++)
    {
        delete[] updated[i];
    }

    delete[] updated;

    return;
}

int main()
{
    int lexiconSize = 3;
    char** lexicon;
    char word[MAX] = {};

    // initialize lexicon for testing:
    lexicon = new char*[lexiconSize];
    lexicon[0] = new char[MAX];
    strcpy_s(lexicon[0], MAX, "maybe");
    lexicon[1] = new char[MAX];
    strcpy_s(lexicon[1], MAX, "this");
    lexicon[2] = new char[MAX];
    strcpy_s(lexicon[2], MAX, "works");

    cout << "enter the word to add" << endl;
    cin >> word;

    newStr(lexicon, lexiconSize, word);

    // menu system that allows to add/delete/print the words

    // delete the lexicon at the end of the program
    for (int i = 0; i < lexiconSize; i++)
    { // delete the internal words
        if (lexicon[i])
        {
            delete[] lexicon[i];
        }
    }

    if (lexicon)
    {
        delete[] lexicon;
    }

    return 0;
}

person Tal J    schedule 14.01.2018    source источник
comment
Ваша самая большая ошибка заключается в том, что вы не используете std::vector.   -  person StoryTeller - Unslander Monica    schedule 14.01.2018
comment
И даже если вы должны использовать необработанные указатели, нет причин не копировать указатели из существующего массива в новый, если вы сделаете это, во время вставки необходимо удалить только существующий массив (а не содержащиеся в нем значения указателя).   -  person SoronelHaetir    schedule 14.01.2018
comment
Должен использовать указатели. Я попробую просто скопировать указатели, спасибо.   -  person Tal J    schedule 14.01.2018
comment
Словарь? std::map<std::string,std::string>   -  person    schedule 14.01.2018
comment
@TalJ: std::map и std::string внутренне используют указатели для вас.   -  person Christian Hackl    schedule 14.01.2018
comment
Я должен что-то упустить. Во время отладки я вижу, что новый словарь создается правильно. Проблема возникает, когда я освобождаю память. Моя структура правильная?: 1) Создать новый словарь с обновленным размером 2) Скопировать указатели из старого словаря в новый 3) Добавить новое слово в конец нового словаря (путем создания нового char[MAX ] До этого момента я вижу, что все работает нормально, и я даже могу отсортировать этот лексикон и распечатать слова. Как с этого момента мне быть с памятью? Что нужно освобождать, а что нет? Спасибо   -  person Tal J    schedule 14.01.2018
comment
Почему вы должны использовать необработанные указатели? Зачем вообще что-то выделять, если размер фиксирован?   -  person Passer By    schedule 14.01.2018
comment
@PasserBy Размер не фиксирован. Лексикон должен изменить свой размер в зависимости от количества слов. Я могу добавить или удалить слова.   -  person Tal J    schedule 14.01.2018


Ответы (2)


Ваша проблема в том, что lexicon передается newStr() по значению.

Таким образом, назначение lexicon = updated невидимо для вызывающего абонента.

Поскольку функция освобождает всю динамически выделенную память, на которую ссылается lexicon[i], все последующие использования lexicon в main() поэтому имеют неопределенное поведение.

Между прочим, вся память, выделенная внутри newStr(), утекает — ни одна переменная не ссылается на нее после возврата из функции, поэтому она не может быть освобождена в коде.

Вместо того, чтобы пытаться использовать указатели и оператор new напрямую, найдите стандартные контейнеры (std::vector) и std::string (для управления строковыми данными).

person Peter    schedule 14.01.2018
comment
Если я правильно понимаю, лексикон передается по ссылке, так как это указатель. Если нет, то как мне передать его по ссылке, а не по значению? Я должен использовать указатели. - person Tal J; 14.01.2018
comment
Вы неправильно понимаете. lexicon — это указатель (переменная, значение которой является адресом переменной), и он передается по значению. *lexicon и **lexicon (поскольку lexicon является указателем на указатель) могут использоваться как ссылки. - person Peter; 14.01.2018

  • Это не код C++: это C с небольшим количеством синтаксического сахара.
  • Не освобождайте что-то внутри функции, если вы создали это в другом месте.
  • Используйте умные указатели.
  • Вы действительно думаете, что ваша реализация будет более эффективной и пригодной, чем std::vector/std::map?
person A.N.    schedule 14.01.2018
comment
К сожалению, я должен использовать указатели для этого. Что касается освобождения памяти внутри функции, как мне воссоздать лексикон без освобождения памяти? - person Tal J; 14.01.2018
comment
Используйте классы для инкапсуляции lexicon. - person A.N.; 14.01.2018
comment
не могли бы вы подробнее объяснить, что вы имеете в виду? Спасибо @A.N. - person Tal J; 14.01.2018
comment
Если вы пишете на C++, вам нужно использовать другой дизайн, не такой, как в C. Используйте класс для реализации lexicon. Создайте класс с приватной переменной для хранилища lexicon, создайте метод распределителя хранилища, метод освобождения хранилища. После этого напишите метод перераспределения, используя allocator+copy+deallocator (потом его можно будет оптимизировать, если очень нужно). Метод добавления слова будет прост в написании. - person A.N.; 14.01.2018