динамическое выделение двойного указателя в c

Этот вопрос является продолжением этот вопрос.

Вот код:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int ch;
    char *ptrChFromFile;
    char **ptrWords;
    int strSize = 1;
    int i;
    int j = 0;
    int numberOfWords = 1;

    ptrChFromFile = malloc(sizeof(char));

    if (ptrChFromFile == NULL)
    {
        puts("COULDN'T ALLOICATE MEMORY");
        exit(EXIT_FAILURE);
    }

    while ((ch = getchar()) != '\n')
    {
        ptrChFromFile = realloc(ptrChFromFile, (strSize+1) * sizeof(char));

        if (ptrChFromFile == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }

        if (ch == ' ')
        {
            numberOfWords++;
        }

        ptrChFromFile[strSize] = ch;
        strSize++;
    }

    ptrChFromFile[strSize] = 0;

    ptrWords = malloc(sizeof(char*) * numberOfWords); //creates number of slots in ptr

    if (ptrWords == NULL)
    {
        puts("failed to allocate memory");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < numberOfWords; i++) // allocates number of bytes in each slot.
    {
        ptrWords[i] = malloc(sizeof(char*)* strSize);
        if (ptrWords[i] == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }
    }

    for (i = 0; i < strSize; i++)
    {
        if (ptrChFromFile[i] != ' ')
        {
            ptrWords[j] = &ptrChFromFile[i];
        }
        else
        {
            ptrWords[j] = 0;
            j++;
        }
    }

    for (i = 0; i < numberOfWords; i++) // free's each slot in ptrWords
    {
        free(ptrWords[i]);
    }

    free(ptrChFromFile);
    free(ptrWords);
    return 0;
}

Я пытаюсь динамически выделить указатель на двойной символ ptrWords. Позвольте мне объяснить ход моих мыслей:

ptrWords = malloc(sizeof(char*) * numberOfWords); //creates number of slots in ptr

Это создает количество слотов (индексов) в ptrWords. поэтому, если у меня есть 3 слова, ptrWords должен выглядеть так:

ptrWords[индекс 0]

ptrWords[индекс 1]

ptrWords[индекс 2]

for (i = 0; i < numberOfWords; i++) // allocates number of bytes in each slot.
{
    ptrWords[i] = malloc(sizeof(char*)* strSize);
    if (ptrWords[i] == NULL)
    {
        puts("failed to allocate memory");
        exit(EXIT_FAILURE);
    }
}

Этот цикл for выделяет каждому слоту в ptrWords память, равную общему количеству символов во входном файле. Таким образом, если во входном файле всего 26 символов, то каждому слоту в ptrWords будет отведено 26 байтов.

ptrWords[index 0] имеет 26 байт памяти

ptrWords[index 1] имеет 26 байт памяти

ptrWords[index 2] имеет 26 байт памяти

Я думаю, что мое выделение памяти для ptrWords правильное, но я не уверен.

for (i = 0; i < strSize; i++)
{
    if (ptrChFromFile[i] != ' ')
    {
        ptrWords[j] = &ptrChFromFile[i];
    }
    else
    {
        ptrWords[j] = 0;
        j++;
    }
}

Этот цикл for должен брать символы из ptrChFromFile и сохранять их в ptrWords как отдельные слова. Моя логика с циклом выглядит следующим образом:

1) Пока ch не равен пробелу, возьмите этот символ и сохраните его в первой позиции (индекс 0) ptrWords.

2) если ch действительно равен пробелу, поместите на его место завершающий символ ('\0'), затем увеличьте j на 1, чтобы перейти к следующему индексу в ptrWords для сохранения следующего слова.

Я использовал отладчик, чтобы выполнить код, но я все еще не могу понять, что не так, поэтому любая помощь будет оценена по достоинству.

Спасибо


Моя реализация:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int ch;
    char *ptrChFromFile;
    char **ptrWords;
    int strSize = 1;
    int i;
    int j = 0;
    int k = 0;
    int numberOfWords = 1;

    ptrChFromFile = malloc(sizeof(char));

    if (ptrChFromFile == NULL)
    {
        puts("COULDN'T ALLOCATE MEMORY");
        exit(EXIT_FAILURE);
    }

    while ((ch = getchar()) != '\n')
    {
        ptrChFromFile = realloc(ptrChFromFile, (strSize+1) * sizeof(char));

        if (ptrChFromFile == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }

        if (ch == ' ')
        {
            numberOfWords++;
        }

        ptrChFromFile[strSize] = ch;
        strSize++;
    }

    ptrChFromFile[strSize] = 0;

    ptrWords = malloc(sizeof(char*) * numberOfWords); //creates number of slots in ptrWords

    for (i = 0; i < numberOfWords; i++) // allocates number of bytes in each slot.
    {
        ptrWords[i] = malloc(sizeof(char*)* strSize);
        if (ptrWords[i] == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }
    }

    if (ptrWords == NULL)
    {
        puts("failed to allocate memory");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < strSize; i++)
    {
        if (ptrChFromFile[i] != ' ')
        {
            ptrWords[j][k++] = ptrChFromFile[i];
        }
        else
        {
            ptrWords[j][k] = 0;
            ptrWords[j] = realloc(ptrWords[j], k+1);
            j++;
            k = 0;
        }
    }
    printf("%s", ptrWords[0]);

    free(ptrChFromFile);
    free(ptrWords);

    return 0;
}

пример ввода: "привет"

вывод: привет

Привет


Текущая версия кода:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


int getStrLength(char *word)
{
    int lengthOfWord = 0;
    int i;

    for (i = 0; word[i] != 0; i++)
    {
        lengthOfWord++;
    }
    return lengthOfWord;
}

int compareWords(char *firstWord, char *secondWord)
{
    while (*firstWord && *firstWord == *secondWord)
    {
        firstWord++;
        secondWord++;
    }
    return *firstWord - *secondWord;
}

int main(void)
{
    int ch;
    char *ptrChFromFile;
    char **ptrWords;
    char **ptrCrunchWord;
    int strSize = 0;
    int i;
    int j = 0;
    int k = 0;
    int numberOfWords = 0;
    int defaultWordLength = 6;

    srand(time(0)); // Use current time as seed for random generator

    ptrChFromFile = malloc(sizeof(char));

    if (ptrChFromFile == NULL)
    {
        puts("COULDN'T ALLOCATE MEMORY");
        exit(EXIT_FAILURE);
    }

    while ((ch = getchar()) != '\n') // this reads in chars from file to ch variable
    {
        ptrChFromFile = realloc(ptrChFromFile, (strSize+1) * sizeof(char));

        if (ptrChFromFile == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }

        if (ch == ' ')
        {
            numberOfWords++;
        }

        ptrChFromFile[strSize] = ch;
        strSize++;
    }

    numberOfWords++;
    ptrChFromFile[strSize] = 0;

    ptrWords = malloc(sizeof(char*) * numberOfWords); //creates number of slots in ptrWords

    if (ptrWords == NULL)
    {
        puts("failed to allocate memory");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < numberOfWords; i++) // allocates number of bytes in each slot.
    {
        ptrWords[i] = malloc(strSize);
        if (ptrWords[i] == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }
    }

    for (i = 0; i < strSize; i++) // This inserts words in ptrWords separated by spaces.
    {
        if (ptrChFromFile[i] != ' ')
        {
            ptrWords[j][k++] = ptrChFromFile[i];
        }
        else
        {
            ptrWords[j][k] = 0;
            ptrWords[j] = realloc(ptrWords[j], k+1);
            j++;
            k = 0;
        }
    }
    // terminate and resize last word
    ptrWords[j][k] = 0;
    ptrWords[j] = realloc(ptrWords[j], k+1);
    j = 0;
    k = 0;

    // crunchWord code starts here:
      ptrCrunchWord = malloc(sizeof(char*));
      ptrCrunchWord[0] = malloc(strSize);

      if (ptrCrunchWord == NULL || ptrCrunchWord[0] == NULL)
       {
           puts("failed to allocate memory");
           exit(EXIT_FAILURE);
       }


    for (i = 0; i < numberOfWords; i++)
    {
        int randomIndex = rand() % numberOfWords;

    if (compareWords(ptrCrunchWord[i], ptrWords[randomIndex]) != 0) 
    {
        if (getStrLength(ptrWords[randomIndex]) >= defaultWordLength) 
        {
            ptrCrunchWord[i] = ptrWords[randomIndex]; // main problem here
        }
    }
}

    printf("The crunch word is: %s", ptrCrunchWord[0]);


    for (i = 0; i < numberOfWords; i++) // Free's allocated memory from all pointers
    {
        free(ptrWords[i]);
    }

    free(ptrChFromFile);
    free(ptrWords);
    free(ptrCrunchWord[0]);
    free(ptrCrunchWord);

    return 0;
}

Это самый актуальный код. Последнее, что мне нужно сделать, это сохранить все слова, которые больше или равны шести, в ptrCrunchWord. Моя основная проблема заключается в выделении места в ptrCrunchWord[0] для последнего ключевого слова и сохранении слов в индексе 0. Я выделяю место только для элемента, потому что в ptrCrunchWord будет храниться только одно слово. Я написал два метода: один проверяет длину каждого слова, а другой метод сравнивает два слова, чтобы увидеть, совпадают ли они. Наконец, мне нужно напечатать ключевое слово без пробелов.

Спасибо


person Mario    schedule 10.03.2019    source источник
comment
С какой проблемой вы столкнулись?   -  person alk    schedule 10.03.2019
comment
У вас большая утечка памяти. Сначала вы выделяете память для каждого элемента ptrWords, но в следующем цикле эти указатели заменяются указателями на ptrChFromFile. Вы имели в виду скопировать строки из одного массива в другой вместо того, чтобы назначать указатели?   -  person Barmar    schedule 10.03.2019
comment
И после того, как вы присвоите все эти указатели, они больше не будут действительны для free(ptrWords[i]), так как эти указатели не были возвращены malloc/realloc.   -  person Barmar    schedule 10.03.2019
comment
@alk извините, если это было непонятно, когда я пытался напечатать первый элемент в ptrWords, используя printf(%s, ptrWords[0]); Я получаю (null), поэтому слова не сохраняются правильно в указателе.   -  person Mario    schedule 10.03.2019
comment
@Barmar Я хочу скопировать символы из ptrChWordsFromFile в ptrWords как отдельные слова, разделенные пробелами. Например, эй, в ptrWords будет: эй (индекс 0) | там (индекс 1). Мне приходится использовать указатели, потому что я читаю ввод из текстового файла.   -  person Mario    schedule 10.03.2019
comment
Почему вы хотите выделить новую память для слов вместо использования первого буфера, который уже содержит все? Ваш первый подход был неплох, но вы забыли завершить слова в буфере. Сколько слов вы хотите потом сложить вместе? Вы используете numberOfWords, который является общим количеством слов в файле. Это, вероятно, неверно, так как включает слишком короткие слова. Кстати: Почему вы вообще храните слова, которые недостаточно длинны в вашем массиве слов?   -  person Gerhardh    schedule 13.03.2019
comment
Вы также можете добавить обработку нескольких пробелов подряд...   -  person Gerhardh    schedule 13.03.2019
comment
Мне нужна помощь в написании функции, которая будет удалять повторяющиеся слова   -  person Mario    schedule 13.03.2019
comment
Мой ответ ниже позволяет избежать дубликатов.   -  person Gerhardh    schedule 13.03.2019


Ответы (2)


Этот цикл неверен:

for (i = 0; i < strSize; i++)
{
    if (ptrChFromFile[i] != ' ')
    {
        ptrWords[j] = &ptrChFromFile[i];
    }
    else
    {
        ptrWords[j] = 0;
        j++;
    }
}

Вы не должны переназначать ptrWords[j], вы должны скопировать в память, которую вы выделили в предыдущем цикле. Вам нужна еще одна переменная k для хранения индекса, который вы назначаете в целевом массиве.

int k = 0;
for (i = 0; i < strSize; i++)
{
    if (ptrChFromFile[i] != ' ')
    {
        ptrWords[j][k++] = ptrChFromFile[i];
    }
    else
    {
        ptrWords[j][k] = 0;
        j++;
        k = 0;
    }
}

Вы также переборщили с объемом памяти, который вы выделили для ptrWords. На каждое слово выделяется столько символов, сколько занимает весь размер файла. Когда вы достигаете конца каждого слова, после назначения ptrWords[j][k] = 0 вы можете уменьшить это выделение до размера слова с помощью:

ptrWords[j] = realloc(ptrWords[j], k+1);

Другая проблема заключается в том, что вы инициализировали strSize = 1;. Это приводит к тому, что вы помещаете первый символ ввода в ptrChFromFile[1] вместо ptrChFromFile[0], поэтому первое слово не копируется правильно. Он должен быть инициализирован как int strSize = 0. Но чтобы скорректировать это изменение, вам нужно увеличить все выделения ptrChFromFile на 1 символ (или сделать еще realloc в конце, чтобы добавить место для завершающего нуля).

Когда вы выделяете память для ptrWords[i], вы не должны умножать на sizeof(char *). ptrWords — массив указателей, ptrWords[i[ — массив char.

После того, как вы закончите цикл, который считывает начальный ввод в ptrChFromFile, вам нужно увеличить numberOfWords. В противном случае вы не будете считать последнее слово перед новой строкой.

Вы не должны были удалять цикл в конце, который освобождает все ptrWords[i]. Все, что вы выделяете с помощью malloc, должно быть освобождено.

Вот рабочая версия:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int ch;
    char *ptrChFromFile;
    char **ptrWords;
    int strSize = 0;
    int i;
    int j = 0;
    int k = 0;
    int numberOfWords = 1;

    ptrChFromFile = malloc(2);

    if (ptrChFromFile == NULL)
    {
        puts("COULDN'T ALLOCATE MEMORY");
        exit(EXIT_FAILURE);
    }

    while ((ch = getchar()) != '\n')
    {
        ptrChFromFile = realloc(ptrChFromFile, (strSize+2));

        if (ptrChFromFile == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }

        if (ch == ' ')
        {
            numberOfWords++;
        }

        ptrChFromFile[strSize] = ch;
        strSize++;
    }
    numberOfWords++; // increment for last word

    ptrChFromFile[strSize] = 0;

    ptrWords = malloc(sizeof(char*) * numberOfWords); //creates number of slots in ptrWords

    for (i = 0; i < numberOfWords; i++) // allocates number of bytes in each slot.
    {
        ptrWords[i] = malloc(strSize);
        if (ptrWords[i] == NULL)
        {
            puts("failed to allocate memory");
            exit(EXIT_FAILURE);
        }
    }

    if (ptrWords == NULL)
    {
        puts("failed to allocate memory");
        exit(EXIT_FAILURE);
    }

    for (i = 0; i < strSize; i++)
    {
        if (ptrChFromFile[i] != ' ')
        {
            ptrWords[j][k++] = ptrChFromFile[i];
        }
        else
        {
            ptrWords[j][k] = 0;
            ptrWords[j] = realloc(ptrWords[j], k+1);
            j++;
            k = 0;
        }
    }
    printf("%s\n", ptrWords[0]);

    for (i = 0; i < strSize; i++) {
        free(ptrWords[i]);
    }
    free(ptrChFromFile);
    free(ptrWords);

    return 0;
}
person Barmar    schedule 10.03.2019
comment
Спасибо за вашу помощь. Я отредактировал код в вопросе, чтобы отразить предоставленные вами предложения, но теперь программа вылетает, и при попытке напечатать первый элемент она печатает всю строку эй, там следует печатать только эй, а не эй Спасибо - person Mario; 10.03.2019
comment
Я откатил вашу правку. Читатели должны видеть исходный код, на который я отвечал, иначе ответ не имеет смысла. Если вы хотите показать, как вы пытались это реализовать, добавьте это в конце, не заменяя оригинал. - person Barmar; 10.03.2019
comment
извинения, которые я добавил в свою реализацию до конца. если я удалю цикл for, который выделяет память для каждого [i], программа вылетает. Я согласен с вами, что выделяю много памяти, но не знаю, как это исправить. Кроме того, я не понимаю, почему я не получаю желаемого результата. Спасибо - person Mario; 10.03.2019
comment
Я нашел еще несколько проблем и обновил ответ пояснениями и полным рабочим кодом. - person Barmar; 10.03.2019
comment
Спасибо за помощь ptrWords работает корректно. Мне просто нужна помощь с последней частью этой программы. Я добавил текущую версию кода, который у меня есть, и я объяснил последние несколько проблем, с которыми я столкнулся, любая помощь была бы очень благодарна вам. - person Mario; 11.03.2019
comment
Вы проигнорировали все исправления, которые я сделал вчера в своем последнем обновлении ответа. Сравни свою версию с моей и увидишь проблемы. - person Barmar; 11.03.2019
comment
Самое главное, что он должен начинаться с int strSize = 0;, а не int strSize = 1;. - person Barmar; 11.03.2019
comment
Я исправил свой код так, чтобы он соответствовал вашему, но теперь у меня возникают проблемы со сравнением слова и удостоверением, что оно больше или равно 6, прежде чем добавлять к моему ptrCrunchWord, а также придумать оператор, который проверяет, существует ли слово. в ptrCrunchWord - person Mario; 12.03.2019
comment
Начальное значение 1 было моей ошибкой в ​​связанном вопросе (сейчас исправлено). Он учитывает пространство для завершения 0. Если вместо этого вы начинаете с 0, вам необходимо соответствующим образом настроить распределение памяти. - person Gerhardh; 12.03.2019
comment
Вы правы, я упустил эту деталь. Либо используйте realloc(strSize + 2), либо сделайте еще realloc перед добавлением нуля в конце. Я не знаю, что такое ptrCrunchWord, в вопросе об этом ничего нет. - person Barmar; 12.03.2019
comment
@Barmar Я добавил текущую версию кода в исходное сообщение с вопросом вместе с объяснением последних нескольких проблем. - person Mario; 13.03.2019
comment
Почему ОП должен копировать слова? Выделение новой памяти не требуется, если OP просто хранит указатель. Конечно, они должны завершаться \0 в начальном буфере. Никаких дополнительных malloc, realloc, free и копирования для каждого слова не требуется. - person Gerhardh; 13.03.2019
comment
@Gerhardh В реальной программе это будет зависеть от того, что вы будете делать со строками позже. Очевидно, это всего лишь упражнение по использованию указателей и копированию данных. - person Barmar; 13.03.2019

Я постараюсь показать версию, более похожую на вашу первоначальную версию.

Я пропущу часть чтения файла. Конечно, необходимо применить исправление, связанное с strSize, упомянутым Бармаром. Этот код запускается после чтения вашего файла.

    ptrChFromFile[strSize] = 0;

    ptrWords = malloc(sizeof(char*) * numberOfWords); //creates number of slots in ptr

    if (ptrWords == NULL)
    {
        puts("failed to allocate memory");
        exit(EXIT_FAILURE);
    }

    #define MIN_LENGTH 6
    int start = 0, end = 0;
    numWords = 0; // Start counting again. Only handle words long enough.
    for (i = 0; i < strSize; i++) // Walk the inital array again
    {
        if (ptrChFromFile[i] == ` `)
        {
          end = i;
          if (end - start >= MIN_LENGTH)
          { // Found a word? Store address and terminate.
            ptrWords = &ptrChFromFile[start];
            ptrChFromFile[i] = 0;
            numWords ++;
          }
          // Words that are too short are ignored
          // Also if a second space follows, no new word is counted...

          // Prepare for new word starting at next position.
          start = end = i+1;
        }
    }

    // Maybe one more word without a space afterwards?
    end = i;
    if (end - start >= MIN_LENGTH)
    { // Found a word? Store address and terminate.
      ptrWords = &ptrChFromFile[start];
      // ptrChFromFile[i] = 0; This word is already terminated.
      numWords ++;
    }

    ptrWords = realloc(sizeof(char*) * numberOfWords); // Reduce size to only hold long words

    // Decide number n how many words shall be concatenated
    // Use scanf or any other mechanism...
    int n = numWords / 2; 

Для выбора слов я буду использовать массив для хранения только индекса каждого слова. Он инициализируется с индексом 0..n-1. Для каждого выбранного слова я перемещаю индекс в первое место в массиве и помещаю (бывший) первый элемент в выбранную позицию. После этого первые n элементов содержат случайные значения индексов. Поскольку диапазон случайного выбора уменьшается на каждом шаге, дублирование невозможно. Не требуется сравнения слов.

    // Create an array to hold the indices of chosen words.
    int selectedWords[n];
    for (i = 0; i < n; i++)
      selectedWords[i] = i;

    // Select n words, store index.
    srand(time(0));
    for (i = 0; i < n; i++)
    {     
      // Pick random element 0..n
      int elem = rand()%(n-i);
      int temp;
      temp = selectedWords[i+elem];
      selectedWords[i+elem] = selectedWords[i];
      selectedWords[i] = temp;
    }
    // This first n entries hold the selected words.

    // How long will it be in the end?
    size_t len = 0;
    for (i = 0; i < n; i++)
    {
      len += strlen(ptrWords[selectedWords[n]]);     
    }

    // Get memory for final result...
    char *resultWord = malloc(len+1);
    // TODO: check for NULL
    resultWord[0] = 0;

    for (i = 0; i < n; i++)
    {
      strcat(resultWord, ptrWords[selectedWords[n]]);     
    }
    printf("%s\n", resultWord)

    free(ptrChFromFile);
    free(ptrWords);
    free(resultWord);

    return 0;
}

Код не компилируется и не тестируется.

person Gerhardh    schedule 13.03.2019