замена строки с использованием динамически выделяемой памяти - PullRequest
0 голосов
/ 10 мая 2018

Я использую приведенную ниже функцию для замены подстроки в данной строке

void ReplaceSubStr(char **inputString, const char *from, const char *to)
{
    char *result = NULL;
    int i, cnt = 0;
    int tolen = strlen(to);
    int fromlen = strlen(from);

    if (*inputString == NULL)
        return;

    // Counting the number of times old word
    // occur in the string
    for (i = 0; (*inputString)[i] != '\0'; i++)
    {
        if (strstr((&(*inputString)[i]), from) == &(*inputString)[i])
        {
            cnt++;

            // Jumping to index after the old word.
            i += fromlen - 1;
        }
    }

    // Making new string of enough length
    result = (char *)malloc(i + cnt * (tolen - fromlen) + 1);
    if (result == NULL)
        return;

    memset(result, 0, i + cnt * (tolen - fromlen) + 1);

    i = 0;
    while (&(*inputString))
    {
        // compare the substring with the result
        if (strstr(*inputString, from) == *inputString)
        {
            strncpy(&result[i], to, strlen(to));
            i += tolen;
            *inputString += fromlen;
        }
        else
        {
            result[i++] = (*inputString)[0];
            if ((*inputString)[1] == '\0')
                break;
            *inputString += 1;
        }
    }

    result[i] = '\0';
    *inputString = result;
    return;
}

Проблема с вышеуказанной функцией - утечка памяти. Вся память, выделенная для inputString, будет потеряна после этой строки.

*inputString = result;

, поскольку я использую strstr и перемещаю указатель inputString *inputString += fromlen; inputString указывает на NULL перед строкой выше. Так как справиться с утечкой памяти здесь.

Примечание: я не хочу возвращать новую память, выделенную внутри функции. Мне нужно изменить память inputString на основе новой длины.

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

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

С текущим API, ReplaceSubStr должен вызываться с адресом указателя на блок, выделенный с malloc() или подобным.Передача указателя на локальное хранилище или строковый литерал будет иметь неопределенное поведение.

Вот несколько идей по улучшению:

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

    char *ReplaceSubStr(const char *inputString, const char *from, const char *to);
    
  • Если строка from пуста, вы должны либо вставить строку toмежду каждым символом входной строки или ничего не делать.После публикации ваш код имеет неопределенное поведение для этого случая границы.

  • Чтобы проверить, присутствует ли строка from со смещением i, используйте memcmp вместо strstr.
  • Если cnt равно 0, делать нечего.
  • Вы должны вернуть состояние ошибки для вызывающей стороны, чтобы определить, может ли память быть выделена или нет.
  • Существуетнет необходимости инициализировать массив result.
  • избегать использования strncpy().Эта функция имеет противоречивую семантику и часто используется неправильно.Читать это: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

Вот улучшенная версия:

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

int ReplaceSubStr(char **inputString, const char *from, const char *to) {
    char *input = *inputString;
    char *p, *q, *result;
    size_t cnt;
    size_t tolen = strlen(to);
    size_t fromlen = strlen(from);

    if (input == NULL || fromlen == 0)
        return 0;

    // Counting the number of times old word occurs in the string
    for (cnt = 0, p = input; (p = strstr(p, from)) != NULL; cnt++) {
        p += fromlen;
    }
    if (cnt == 0)   // no occurrence, nothing to do.
        return 0;

    // Making new string of enough length
    result = (char *)malloc(strlen(input) + cnt * (tolen - fromlen) + 1);
    if (result == NULL)
        return -1;

    for (p = input, q = result;;) {
        char *p0 = p;
        p = strstr(p, from);
        if (p == NULL) {
            strcpy(q, p0);
            break;
        }
        memcpy(q, p0, p - p0);
        q += p - p0;
        memcpy(q, to, tolen);
        q += tolen;
        p += fromlen;
    }
    free(*inputString);
    *inputString = result;
    return 0;
}

int main() {
    char *p = strdup("Hello world!");
    ReplaceSubStr(&p, "l", "");
    printf("%s\n", p);  // prints Heo word!
    free(p);
    return 0;
}
0 голосов
/ 10 мая 2018

Очевидно, что вы не можете освободить ввод, поскольку он может быть литералом, часть памяти, которую вы не контролируете.Это нанесло бы ущерб вашей функции даже больше, чем сейчас.

Вы можете вернуть старое значение inputString, чтобы иметь возможность освободить его при необходимости.

char *ReplaceSubStr(char **inputString, const char *from, const char *to)
{
    char *old_string = *inputString;
    ...
    return old_string;
}

Звонящийотвечает за освобождение содержимого old_string, если это необходимо.

Если не нужно (мы должны обойти ввод char **, назначив допустимый доступный для записи массив указателю, чтобы можно было передать этот указатель:

char input[]="hello world";
char *ptr = input;
ReplaceSubStr(&ptr, "hello", "hi");
// input is now "hi world" in a different location
free(ptr);  // when replaced string isn't needed

если необходимо:

char *input = strdup("hello world");
char *old_input = ReplaceSubStr(&input, "hello", "hi");
free(old_input);

или просто

free(ReplaceSubStr(&input, "hello", "hi"));

тогда всегда (когда заменяемая строка не требуется):

free(input);

Единственное ограничение заключается в том, что вы не можете использовать постоянный строковый литерал в качестве входных данных (const char *input = "hello world") из-за прототипа и возможного возврата char * для передачи в free.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...