Функция разделения с использованием C и динамического распределения памяти

Я запрограммировал функцию для разделения строк, и она дает ожидаемый результат. Я с нетерпением жду возможности написать лучший код, и мне сказали, что это хорошее место для начала.

Вот моя программа:

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

char **split(char *str) {
    int str_length = 0;
    int i = 0;
    int separator_count = 0;
    while (str[i] != ' ') {
        if (str[i] == ' ') {
            separator_count++;
        }
        i++;
    }

    char **word_array;

    word_array = malloc((separator_count + 2) * sizeof(char *));  

    int word_len = 0;
    int separator_index = 0;
    int b = 0;                                     //to iterate over "str"
  
    for (int a = 0; a < (separator_count + 2); a++) {
        word_len = 0;
        while (str_length < strlen(str) + 1) {
            str_length++;

            if (str[b] == ' ' || str[b] == ' ') {
                separator_index = b;
                word_array[a] = malloc(word_len * sizeof(char));
                int word_depth = 0;
                for (int c = (separator_index - word_len); c < separator_index; c++) {
                    chrctr = str[c];
                    word_array[a][word_depth] = str[c];
                    word_depth++;
                }
                word_array[a][word_len] = ' ';         //terminate found and stored word with null charachter
                word_len = 0;                           //reset word length counter to 0
                b++;                                    //skip to next charachter
                break;                                  // break to go to next index of word_array
            }
            word_len++;
            b++;
        }
    }
    word_array[separator_count + 2] = NULL;
    return word_array;
}

int main() {
    char s[] = "A complete toolkit for building search into your product.";  //test string
    char **ss = split(s);
    for (int i = 0; i < sizeof(ss); i++) {        //here is the problem, and doing sizeof(ss)/sizeof(ss[0])=1 !!
        for (int j = 0; j < strlen (ss[i]); j++) {
            printf("%c", ss[i][j]);
        }
        printf ("n");
    }
}

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

2 ответа
2

Дизайн: Код должен объяснять случаи

  1. Двойной пробел: "abc xyz" -> Это будет 2 или 3 жетона?
  2. Ведущее место: " abc xyz" -> Это будет 2 или 3 жетона?
  3. Конечный пробел: "abc xyz " -> Это будет 2 или 3 жетона?

Дизайн: Обобщение

split() только считает ' '. Может быть, все пробелы, 't', 'n', …?

Или передайте в функцию список разделителей: split(char *str, const char *separators)

Дизайн: const

Согласно данным, на которые ссылается str не меняется, считайте char **split(const char *str) для большей применимости и самостоятельной документации.

Ошибка: недостаточно памяти

// word_array[a] = malloc(word_len * sizeof(char));
word_array[a] = malloc((word_len + 1u) * sizeof(char));
...
word_array[a][word_len] = ' ';

Ошибка: запись вне распределения

word_array = malloc((separator_count + 2) * sizeof(char *));  
...
word_array[separator_count + 2] = NULL; //OOPS!

// I suspect OP wanted  
word_array[separator_count + 1] = NULL;

Распределите по размеру ссылочного объекта, а не по типу

// word_array = malloc((separator_count + 2) * sizeof(char *)); 
word_array = malloc(sizeof *word_array * (separator_count + 2u)); 


word_array[a] = malloc(word_len * sizeof(char));
word_array[a] = malloc(sizeof (word_array[a][0] * word_len);

Легче правильно кодировать, проверять и поддерживать.

Использование самых широких типов при умножении предотвращает переполнение — хотя в данном случае это не так важно.

Надежный код проверяет успешность выделения

ptr = malloc(sizeof *ptr * n);
if (ptr == NULL && n > 0) {
  HandleOutOfMemory(); // Some user code
}

Распределение по уборке

main() называется split(s) который выделяет, но не может вызвать сопоставление free(). Не особо беспокоюсь о main() поскольку код все равно заканчивается, но это хорошая практика.

Незначительный: int word_len недостаточно для больших струн

// int word_len 
size_t word_len 

    Сначала я рассмотрю вашу проблему в основной функции. Где вы отметили свою проблему, sizeof(ss) не возвращает количество элементов, он возвращает размер переменной ss, которая является указателем. Это то же самое, что и sizeof(char *) (обычно 8 на большинстве платформ). Это немного сбивает с толку, но знайте, что char s[] и char *s не то же самое. Что вам нужно, так это способ получить количество разбиений в возвращенном результате. Есть несколько способов сделать это, один из которых — переопределить сигнатуру функции, чтобы включить вывод, а затем вернуть целочисленный тип, обозначающий количество разбиений:

    int split(const char *input, char **output);
    

    Однако есть способ сделать это, даже не зная, сколько разделений существует. Вы вроде как реализовали это, но не используете. Подобно тому, как обнаруживается конец c-строк, вы можете обнулить последний байт массива, чтобы указать конец списка (что, как я вижу, вы уже сделали в конце функции разделения — там является проблема с неправильной индексацией последнего элемента, но я расскажу об этом позже). Итак, вместо того, чтобы ваш цикл завершился условием i == num_splits, вы можете просто завершить работу при обнаружении нулевого указателя:

    int main() {
        char s[] = "A complete toolkit for building search into your product.";  //test string
        char **ss = split(s);
        for (int i = 0; ss[i]; i++) {
            for (int j = 0; j < strlen (ss[i]); j++) {
                printf("%c", ss[i][j]);
            }
            printf ("n");
        }
    }
    

    Внесение этой поправки должно решить вашу проблему без необходимости что-либо менять с помощью функции разделения.

    Что касается проблемы с индексированием, которая у вас есть (в нескольких местах), вы отказываетесь от одного. Помня, что массивы индексируются 0, последний элемент массива индексируется по размеру минус 1. Итак, строки
    word_array[a][word_len] = ' ';, и
    word_array[separator_count + 2] = NULL;
    индексируются за пределами выделенной памяти. Первая строка на самом деле в порядке, вам просто нужно исправить ее в распределении: word_array[a] = malloc((word_len + 1) * sizeof(char)); Вторую строку нужно изменить на word_array[separator_count + 1] = NULL;.

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

    Что касается кода функции разделения, есть некоторые вещи, которые его немного улучшат. Я не буду давать вам исчерпывающий список, тем более, что некоторые предложения могут быть более субъективными, но вот некоторые, которые, я думаю, вы можете найти полезными:

    1. Я считаю, что это просто то, что вы упустили из виду, но chrchr переменная никогда не объявляется (используется в строке chrctr = str[c];). Я предполагаю, что его все равно выбросят, так как вы просто используете str[c] прямо в следующей строке.
    2. С помощью strlen в вашем цикле while обычно не является хорошей практикой, поскольку компилятор может выплюнуть код, который вызывает его больше, чем необходимо. Фактически, вы можете полностью избавиться от него, так как вы можете получить длину строки из своего первого цикла, в котором вы подсчитываете количество разделителей (у вас уже есть длина строки в пределах i Переменная).
    3. Вы можете избежать печати каждого символа за раз и, поскольку вы обнулили последний байт в конце каждого слова, печатать каждое слово, используя print("%sn", ss[i]);.
    4. Вы можете еще многое сделать, чтобы улучшить это, но я не хочу перегружать вас слишком большим количеством информации. Но я закончу заключительным замечанием о том, что вам следует рассмотреть структуру, отличную от динамически выделяемых массивов внутри динамически выделяемого массива. Все это динамическое распределение должно быть очищено, когда оно будет использовано, и управление этой памятью только добавляет больше сложности и возможных осложнений (не говоря уже о том, что оно очень подвержено ошибкам).

      Добавить комментарий

      Ваш адрес email не будет опубликован. Обязательные поля помечены *