Как корректно высвободить память после работы функции?

У меня имееться функция,которая к исходной строке,преклеивает строку:

char *concat(char *str1, char *str2) {
    char *str = malloc(strlen(str1) + strlen(str2) + 1);
    strcpy(str, str1);
    strcat(str, str2);
    return str;
}

Код который я использую:

int main() {
    char *string = "str1";
    char *pattern = "str2";

    for (int i = 0; i < 10; i++) {
        pattern = concat(pattern, string);
        puts(pattern);
    }

    free(pattern);
    return 0;
}

Этот код дает мне верный вывод:

str2str1
str2str1str1
str2str1str1str1
str2str1str1str1str1
str2str1str1str1str1str1
str2str1str1str1str1str1str1
str2str1str1str1str1str1str1str1
str2str1str1str1str1str1str1str1str1
str2str1str1str1str1str1str1str1str1str1
str2str1str1str1str1str1str1str1str1str1str1

Проблема: я не могу очистить память выделенную malloc().Я знаю причину,вся моя строка записывается в разные адресса, поэтому, я решил использовать realloc(), но и тут потерпел крах,память течёт:

char *concat_rs(char *str,char *str1, char *str2) {
    str = realloc(str, strlen(str1) + strlen(str2) + 1);
    strcat(str, str1);
    strcat(str,str2);
    return str;
}

Да,память мне нужно почистить одним куском после работы.


Ответы (3 шт):

Автор решения: eri

Первый pattern проинициализируй через malloc. Указатель на константу тут как-то не очень. Потом все просто.

for (int i = 0; i < 10; i++) {
    char* res = concat(pattern, string);
    free(pattern);
    pattern=res;
    puts(pattern);
}
→ Ссылка
Автор решения: Stanislav Volodarskiy

В C указатели бывают владеющие и не владеющие. Указатель владеет памятью если его можно передать во free. Иначе он памятью не владеет, а только ссылается на неё.

На первой итерации цикла pattern не владеет строкой - её нельзя удалять - она константа. Начиная со второй итерации pattern владеет строками на которые указывает. Соответственно надо переделать код:

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

char *concat(char *str1, char *str2) {
    char *str = malloc(strlen(str1) + strlen(str2) + 1);
    strcpy(str, str1);
    strcat(str, str2);
    return str;
}

int main() {
    char *string = "str1";
    char *pattern = "str2";

    for (int i = 0; i < 10; i++) {
        if (i == 0) {
            pattern = concat(pattern, string);
        } else {
            char *tmp = concat(pattern, string);
            free(pattern);
            pattern = tmp;
        }
        puts(pattern);
    }

    free(pattern);
    return 0;
}

Это будет работать без утечек памяти. Но лучше никогда не класть в одну переменную владеющие и не владеющие указатели. Никогда.

Ещё одна вещь: компилятор C позволяет конструкцию char *p = "string"; ради обратной совместимости с унаследованным кодом. Всегда в подобных случаях пишите const - менять память на которую показывает p вы не можете.

Законстантить придётся и аргументы функции. Это хорошо - обещаем что аргументы мы не трогаем, в частности, не попытаемся их освободить:

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

char *concat(const char *s1, const char *s2) {
    char *s = malloc(strlen(s1) + strlen(s2) + 1);
    strcpy(s, s1);
    strcat(s, s2);
    return s;
}

int main() {
    const char *s = "str1";
    char *pattern = concat("", "str2");

    for (int i = 0; i < 10; i++) {
        char *tmp = concat(pattern, s);
        free(pattern);
        pattern = tmp;
        puts(pattern);
    }

    free(pattern);
    return 0;
}

Хотите работать с realloc? Можно, только помните что он считает что первый аргумент - владеющий указатель. Константную строку туда передавать нельзя. Сперва её надо выделить. Например так:

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

char *concat(char *s1, const char *s2) {
    char *res = realloc(s1, strlen(s1) + strlen(s2) + 1);
    if (res == NULL) {
        exit(1);
    }
    strcat(res, s2);
    return res;
}

int main() {
    const char *s = "str1";
    char *pattern = calloc(1, 1); // пустая строка
    pattern = concat(pattern, "str2");

    for (int i = 0; i < 10; i++) {
        pattern = concat(pattern, s);
        puts(pattern);
    }

    free(pattern);
    return 0;
}
→ Ссылка
Автор решения: MiniMax

Можно сделать так:

  1. Выделять память в main и передавать указатель на буфер в concat, как это делается в strcat.
  2. Использовать realloc, который сам будет следить за освобождением памяти (при необходимости) по старому указателю и возвращать новый указатель.

В итоге память легко освобождается в конце main одним free(). Проверку результата malloc добавлять не стал, чтобы не загромождать, а проверку realloc добавил для демонстрации того, что новый указатель не должен затирать старый на случай неудачного вызова realloc.

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

void concat(char *str_dest, const char *str_src) {
    strcat(str_dest, str_src);
}

int main(void) {
    const char *string = "str1";
    const char *pattern = "str2";
    size_t string_len = strlen(string);

    size_t str_accum_len = strlen(pattern) + 1;
    char *str_accum = malloc(str_accum_len);
    strcpy(str_accum, pattern);

    for (size_t i = 0; i < 10; i++) {
        str_accum_len += string_len;  
        char *tmp = realloc(str_accum, str_accum_len);

        if (tmp) {
            str_accum = tmp;
        } else {
            puts("Realloc fails"); 
            exit(EXIT_FAILURE);
        }

        concat(str_accum, string);
        puts(str_accum);
    }

    free(str_accum);
    return 0;
}
→ Ссылка