Как корректно высвободить память после работы функции?
У меня имееться функция,которая к исходной строке,преклеивает строку:
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 шт):
Первый pattern проинициализируй через malloc. Указатель на константу тут как-то не очень. Потом все просто.
for (int i = 0; i < 10; i++) {
char* res = concat(pattern, string);
free(pattern);
pattern=res;
puts(pattern);
}
В 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;
}
Можно сделать так:
- Выделять память в
mainи передавать указатель на буфер вconcat, как это делается вstrcat. - Использовать
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;
}