free при условии в функции Си
Вот код:
char* newstr(char* str, int n)
{
int len = strlen(str);
if (len >= n)
{
str = str + len - n;
}
if (len < n)
{
char* s = (char*)malloc(n + 1);
if (s != 0) memset(s, '\0', n + 1);
if (s != 0) memset(s, '*', n - len);
if (s != 0) strcat_s(s, n + 1, str);
str = s;
}
return str;
}
Вкратце: если n больше длины строки, то начало новой строки заполняется символом *, а остальное заполняется строкой str. При выполнении этого условия, как видно, выделяется память, которую нужно очистить. Вот вопрос: как её очистить, если выполнилось условие n > len? Как это грамотно реализовать? Или это просто нужно делать условием вне функции: если n > len(2 раз len узнавать не хочется на самом деле), то free()?
UPD: Я сделал так:
printf("%s\n", newstr(stroka, n));
free(newstr(stroka, n));
Правильно ли это? И что будет делать free, если по условию память не выделялась? Скорее всего это бред, ведь снова выделится память, а старая не будет стерта, только новая...
Ответы (3 шт):
Условие задачи понятно, но вот то, как Вы это реализуете - не очень. Несколько замечаний по стили, что бы прояснить:
Вместо оператора if (len < n) надо написать просто else Так гораздо спокойней и понятней.
Вместо операторов
if (s != 0) memset(s, '\0', n + 1);
if (s != 0) memset(s, '*', n - len);
if (s != 0) strcat_s(s, n + 1, str);
Надо просто написать:
if (s != 0) {
memset(s, '\0', n + 1);
memset(s, '*', n - len);
strcat_s(s, n + 1, str);
}
В этих операторах есть явная ошибка. Оба вызова функции memset() пишут в одно и то же место - значение указателя s ведь между вызовами не изменилось.
Ну и последнее. Эти два оператора вместе - нонсенс:
printf("%s\n", newstr(stroka, n));
free(newstr(stroka, n));
Правильно написать вот так:
char *wrk;
wrk = newstr(stroka, n);
printf("%s\n", wrk);
free(wrk);
Назначение указателя в C указывать место в памяти. Разыменовав указатель, можно читать и писать память. Дополнительно некоторые указатели служат для управления памятью. Будем их называть владеющими. Владеющий указатель возращается функциями malloc, calloc или realloc и может быть передан в функции free и realloc. Если указатель не владеющий, то будем его называть невладеющим. Невладеющие указатели могут получены как адрес объекта или элемента массива.
Невладеющие указатели нельзя передавать в функции управления памятью. Владеющий указатель когда-нибудь будет передан в такую функцию: в нормальной программе выделенная память рано или поздно освобождается. Глядя на указатель во время выполнения программы невозможно сказать владеющий он или нет. Поэтому программист должен следовать каким-то правилам, чтобы читая код знать владеет указатель памятью или только ссылается на неё.
Последняя вещь - память, указанную владеющим указателем, можно удалить только один раз. Довольно теории, переходим к практике.
char *copy_string(char *s) {
if (<сложное условие>) {
return s;
}
return malloc(...);
}
Функция copy_string иногда возвращает переданный ей указатель, иногда новый. Новый указатель - владеющий. Про старый указатель такого сказать нельзя. Если он не владеющий, то у нас проблема:
char *s = <невладеющий указатель>;
char *s1 = copy_string(s);
// надо ли удалять s1? Неизвестно.
free(s1);
Если указатель владеющий, то у нас другая проблема:
char *s = <владеющий указатель>;
char *s1 = copy_string(s);
// удаляем s1
free(s1);
// надо ли удалять s? Неизвестно.
free(s);
Если s == s1, то удалять s не надо - нельзя удалять одну память два раза. Иначе удалить s необходимо, чтобы избежать утечки памяти.
Резюме: или вы чётко знаете что такой-то указатель владеющий, а другой - невладеющий или вам нужна дополнительная информация о том как указатели соотносятся во время исполнения программы.
P.S. Иногда вы можете игнорировать вопросы владения и утечки памяти. Это нормальная практика для маленькой утилиты, которая быстро завершается и за которой "убирает" операционная система.
Мне кажется по смыслу даже названия функции нужно всегда возвращать новую строку тогда и не будет возникать вопрос - когда вызывать free();
char* newstr(char* str, int n)
{
int len = strlen(str);
char *retstr = malloc(n+1);
if( len > n){
memcpy(retstr,str+len-n,n);
}
else{
memset(retstr,'*',n-len);
memcpy(retstr+n-len,str,len);
}
retstr[n] = 0;
return(retstr);
}