Простой вопрос о динамическом выделении памяти для указателя на символ - PullRequest
2 голосов
/ 14 мая 2019

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

typedef struct smart_string {
    char *word;
    int length;
} smart_string;

smart_string* create_smart_string(char *str)
{
    smart_string *s = (smart_string*)malloc(sizeof(smart_string));
    s->length = strlen(str);
    s->word = malloc(s->length);
    strcpy(s->word, str);
    return s;
}

Но ответ был такой:

typedef struct smart_string {
    char *word;
    int length;
} smart_string;

smart_string *create_smart_string(char *str)
{
    smart_string *s = malloc(sizeof(smart_string));
    s->length = strlen(str);
    s->word = malloc(sizeof(char) * (s->length + 1));
    strcpy(s->word, str);
    return s;
}

Я пошел по коду: блоки и проверил их оба, чтобы увидеть какие-либо существенные различия.Насколько я знаю, их выходные данные были одинаковыми.

Я сделал свой код таким, какой он есть, потому что я подумал, что если мы выделим определенный блок памяти для s->word, то это должно бытьто же число байтов, что и s ->length, потому что это строка, которую мы хотим скопировать.

Однако правильный ответ ниже умножает sizeof(char) (что составляет всего 1 байт) на s->length + 1.Почему нужно добавить 1 к s->length?Какова важность умножения s->length на sizeof(char)?Какие ошибки я допустил в своем ответе, на которые мне следует обратить внимание?

Ответы [ 2 ]

4 голосов
/ 14 мая 2019

Ваш ответ неверный, так как он не учитывает завершающий '\0' -характер. В C строки заканчиваются на 0. Вот как можно определить их длину. Типичная реализация strlen() будет выглядеть как

size_t strlen(char const *str)
{
    for (char const *p = str; *p; ++p);  // as long as p doesn't point to 0 increment p
    return p - str;  // the length of the string is determined by the distance of
}                    // the '\0'-character to the beginning of the string.

Но оба "решения" - фубар. Зачем выделять структуру, состоящую из int и указателя на свободное хранилище («куча») !? smart_string::length, являющийся int, - это другой вес.

#include <stddef.h>  // size_t

typedef struct smart_string_tag {  // *)
    char *word;
    size_t length;
} smart_string_t;

#include <assert.h>  // assert()
#include <string.h>  // strlen(), strcpy()
#include <stdlib.h>  // malloc()

smart_string_t create_smart_string(char const *str)
{
    assert(str);  // make sure str isn't NULL

    smart_string_t new_smart_string;
    new_smart_string.length = strlen(str);
    new_smart_string.word = calloc(new_smart_string.length + 1, sizeof *new_smart_string.word);

    if(!new_smart_string.word) {
        new_smart_string.length = 0;
        return new_smart_string;
    }

    strcpy(new_smart_string.word, str);
    return new_smart_string;
}

*) Понимание пространств имен C

3 голосов
/ 14 мая 2019

sizeof(char) == 1 по определению, поэтому это не имеет значения.

Вы не должны разыгрывать результат malloc: Должен ли я разыгрывать результат malloc?

И ваша единственная реальная разница в том, что strlen возвращает длину строки, не включая завершающий символ NUL ('\0'), поэтому вам нужно добавить + 1 к размеру буфера, как в решении.

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

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

...