Добавление элемента в массив строк - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть массив строк

char **tab;

и я хочу, чтобы его можно было увеличивать по мере ввода пользователем новых строк. Я создал этот код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
    char **tab=malloc(sizeof(char*));
    tab[0]=malloc(41*sizeof(char));
    int i=0;
    int d=0;
    while(strcmp(tab[i],"END"))
    {
        if(d==0)
        {
            i--;
            d++;
        }
        i++;
        scanf("%s",tab[i]);
        **tab=realloc(tab,(i+2)*sizeof(char*));
        tab[i+1]=malloc(41*sizeof(char));
    }
    return 0;
}

его назначение - читать новые строки на вкладке, пока пользователь не напишет ключевое слово "END". Поскольку неизвестно, сколько слов будет введено, я попытался перераспределить размер массива после каждой итерации. К сожалению, он печатает ошибку сегментации после получения 3 слов. Что я сделал не так? Есть ли лучший способ сделать эту задачу?

Максимальная длина любого слова - 40

.

Ответы [ 2 ]

0 голосов
/ 12 ноября 2018

Ваш алгоритм разбит на два значимых места

  • **tab = ... в теле цикла неверно.Если tab равно char**, то *tab будет char*, а **tab будет char.Присвоение адреса памяти char должно пометить огромные предупреждения из вашей цепочки инструментов, и если он не увеличит ваши уровни предупреждений или не получит новую цепочку инструментов.
  • Первоначальный вход в ваше условие while оценивает неопределенные данные.В то время вы выделили необработанную память для tab[0], но пока еще ничего не заполнено.Таким образом, ваша программа вызывает неопределенное поведение .

Помимо вышесказанного, алгоритмы расширения не сложны, и объяснение вашего rubber-duck поможетзначительно, прежде чем писать какой-либо код.При этом вы увидите, что перераспределение массива указателей при каждом новом чтении является дорогостоящим и неэффективным.Алгоритмы геометрического расширения делают это намного лучше.В качестве бонуса исправлены обе вышеуказанные проблемы.

Код

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

#define MAX_BUFLEN  41
#define MAX_STR_FMT "%40s"

int main()
{
    char **tab = NULL;
    size_t size = 0, capacity = 0;

    char str[MAX_BUFLEN];

    while (scanf(MAX_STR_FMT, str) == 1 && strcmp(str, "END"))
    {
        // check for expansion 
        if (size == capacity)
        {
            size_t new_capacity = 2 * capacity + 1;
            void *tmp = realloc(tab, new_capacity * sizeof *tab);
            if (tmp == NULL)
            {
                perror("Failed to expand dynamic table");
                break;
            }

            // save expanded table, and update capacity
            tab = tmp;
            capacity = new_capacity;
        }

        size_t slen = strlen(str)+1;
        if ((tab[size] = malloc(slen)) == NULL)
        {
            perror("Failed to allocate buffer for new string");
            break;
        }

        // copy incoming string; update 'size' to reflect new count
        memcpy(tab[size++], str, slen);
    }

    //
    // TODO: use 'tab' holding 'size' pointers. 
    //

    // then free the table
    while (size-- > 0)
        free(tab[size]);
    free(tab);

    return 0;
}

Альтернатива: нет указателей на указатели

Если ваша потребность действительно требует выделения фиксированной длины (как показывает ваш код), вам вообще не нужны указатели на указатели (если нет какой-то скрытой повестки дня для чего-то вроде сортировки с заменой указателей) намного более эффективно, чем замена полных строковых буферов).

Код

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

#define MAX_BUFLEN  41
#define MAX_STR_FMT "%40s"

int main()
{
    char (*tab)[MAX_BUFLEN] = NULL; // see difference here
    size_t size = 0, capacity = 0;

    char str[MAX_BUFLEN];

    while (scanf(MAX_STR_FMT, str) == 1 && strcmp(str, "END"))
    {
        // check for expansion 
        if (size == capacity)
        {
            size_t new_capacity = 2 * capacity + 1;
            void *tmp = realloc(tab, new_capacity * sizeof *tab);
            if (tmp == NULL)
            {
                perror("Failed to expand dynamic table");
                break;
            }

            // save expanded table, and update capacity
            tab = tmp;
            capacity = new_capacity;
        }

        // notice no additional allocations here
        strcpy(tab[size++], str);
    }

    //
    // TODO: use 'tab' holding 'size' strings. 
    //

    // then free the table
    free(tab);

    return 0;
}

Сводка

Исправить ваш код было довольно просто, но сделать его лучше тоже.Никогда не переставайте думать о , почему вы делаете то, что делаете, разрабатывая свои алгоритмы, и проводите много времени, разговаривая с rubber-duck .

0 голосов
/ 12 ноября 2018

Проблема заключается в этой строке:

**tab=realloc(tab,(i+2)*sizeof(char*));

Здесь **tab разыменовывает tab на первый символ в первой строке, хранящейся в tab, а это не то, что вам нужно.Попробуйте вместо этого:

tab=realloc(tab,(i+2)*sizeof(char*));

Если вы включите предупреждения компилятора, они должны поймать этот тип ошибки.

...