char *를 리턴해 주는 C 함수들의 메모리 관리는?

Viz의 이미지

char *를 리턴해주는 함수들은 무지 많지요.

기본 C 라이브러리에 있는 strcat, strdup 같은 스트링 관련 함수에서 부터..
ctime같은 편한 녀석들도 있구요.

그런데 언제나 궁금한 것이.. 이렇게 받은 char *에 대해 과연 free를 해주어야 하는가.. 랍니다.

분명히 strdup 같은 경우에는 malloc으로 할당되기 때문에 free를 하라는 지시가 man page에 존재합니다.

그런데 ctime 이란 함수의 경우에는 어떨까요?

char *ctime(const time_t *timep); man page에는 free에 관련된 이야기가 전혀 없네요.

저의 생각으로는 아마 함수 내부에 static으로 선언된 buffer가 존재한다.. 일 껏 같은데요.
그럼 생기는 문제가 있죠. thread unsafe할 것이라는 것..(당연한건가요?)

흐음.. 이런 ctime처럼 특별한 언급이 없다면 저는 언제나 할당된 메모리는 free할 필요가 없고, 해서도 안된다.. 라는 입장이긴 합니다만.. 한 후배가 자신은 free()를 해왔다고 해서.. 당황스럽네요.. ;;

어떻게 하는 것이 정석인가요? :D

정태영의 이미지

free를 해줘야 합니다 =3=33

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

wowcode의 이미지

내부에 static 하게 잡힌 주소를 리턴합니다. free 하실 필요는 없습니다. 그리고 thread safe를 원하시면 ctime_r 을 사용하시면 됩니다.

eungkyu의 이미지

맨페이지를 좀 더 열심히 읽어봐주세요 8)
거의 다 나옵니다. ctime같은 것은 당연히 나옵니다.

이한길의 이미지

저도 궁금했었는데... ㅡㅡ;

위에 둘중 어느 분의 말을 따라야 할까요?

저는 예전에 대충 테스트해본 결과... 두번째분 말씀이 맞는 듯한데..
모르겠습니다..

----
먼저 알게 된 것을 알려주는 것은 즐거운 일이다!
http://hangulee.springnote.com
http://hangulee.egloos.com

맹고이의 이미지

man ctime의 마지막 부분.

NOTES
       The four functions acstime(), ctime(), gmtime() and local-
       time()  return  a pointer to static data and hence are not
       thread-safe.  Thread-safe versions acstime_r(), ctime_r(),
       gmtime_r()  and  localtime_r() are specified by SUSv2, and
       available since libc 5.2.5.

       The glibc version of struct tm has additional fields

              long tm_gmtoff;           /* Seconds east of UTC */
              const char *tm_tm_zone;   /* Timezone abbreviation */

       defined  when  _BSD_SOURCE  was   set   before   including
       <time.h>.   This  is  a  BSD extension, present in 4.3BSD-
       Reno.
pynoos의 이미지

함수마다 다르며, 어떤 함수가 malloc된 buffer를 넘기는지 확인해서 사용해야합니다.

정말 귀찮은 일이죠....

cinsk의 이미지

특별한 언급이 없으면 static 할당된 메모리를 리턴합니다.

윗 분들 중 "free()를 해 줄 필요가 없다"란 말씀을 하셨는데, static으로 잡힌 경우 "free()를 하면 안된다. 큰일난다"로 표현하는게 나을 듯 싶군요. strdup()을 제외하고, malloc()한 memory를 돌려주는 함수는... 흠.. 지금 기억으론 없는 것 같군요.

Viz의 이미지

답변해 주셔서 감사합니다.

후배 녀석 다시 교육시켜야겠네요. :)

My Passion for the Vision!

정태영의 이미지

char* strcat( char* dest, const char* src );

결과적으로 strcat은.. dest를 리턴해주지 않던가요 +_+?
그랬던거 같은데.. 앗 그러고보니 저것도 내부적으로 malloc하는건 아니군요 =3=33

생각없이 glib에 있는 것들 생각하고 free해야 한다고 달아버렸네요 =3=3

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

ㅡ,.ㅡ;;의 이미지

당연히 프리하면안되죠..

조금 생각해보면.. 해야할지 안해도 될지 구분이 갑니다.

물론 확힐한건 스펙을 확인해보고 사용해야겠지만...

strdup같은경우 불특정한 길이를 계속적으로 복사할필요가 있죠.. 그러면 이것이 내부 static 변수로 리턴할리가 있겠어요..ㅋㅋ

ctime 같은경우 겨우 날짜스트링을 반환하는데 내부에서 alloc 한다는건 너무무겁다는생각이 들죠.. 더구나 날짜 스트링은 그크기가 변할필요가 없습니다.
고정길이라는거죠..


----------------------------------------------------------------------------

winchild의 이미지

정확하게 구현되어 있는 소오스를 들여다 본것은 아니고, 운영체제마다 조금씩 다를수 있지만, 이런식의 함수를 호출해서 사용할 경우에는 일단 free 보다도 그 메모리의 데이터가 유지되어 있는것에 대하여 신경을 쓰실필요가 있습니다.

일단 함수가 호출되면, 그 데이터는 HEAP 의 임시영역에 잡힙니다. 그리고 데이터가 만들어지고, HEAP 영역의 데이터 포인터를 리턴하고 종료합니다. 즉 이미 free() 가 된것과 마찬가지의 상태입니다.

그런데 이렇게 HEAP 영역에 잡힌 데이터 영역은 그 함수의 사용이 종료되면 사실 시스템에 반환됩니다. 반환된다고 하면, 데이터가 잃어버릴 염려가 있을지도 모르겠지만, 여기서 주의해야 할 요건이 생기는 것입니다.

HEAP 영역의 메모리가 시스템에 반환 되었다고 하더라도 그 프로세서 가 종료된것은 아니므로, 그 메모리는 계속 그 프로세서가 사용할수 있습니다. 따라서 그다음의 조작에 영향을 받습니다. 다른 함수를 호출하여 그 HEAP 공간을 사용하게 되면 그 데이터는 거의 깨진다고 봐야 합니다.

그러므로 함수를 호출하기전에 데이터를 자신의 메모리 영역으로 옮겨놓는것이 필요할수 있습니다. 포인터 함수이므로 strcpy, memcpy 등으로 옮길수 있는데, 이런함수는 매크로 수준이거나, 함수가 호출되어도 HEAP 공간을 거의 사용하지 않으므로 데이터를 이용할 수 있습니다.

그러나 좀더 복잡한 함수, 예를 들면 printf 같은 것을 호출하면 이때는 거의 100% 깨집니다. 따라서 보통 printf ("%ld%, ctime()) 같은 것은 사용하면 안됩니다. 거의 세그멘테이션 폴트가 발생할것 입니다.

좀 정리가 되셨는지요.

- 겨울아찌 -

- 겨울아찌 -
winchild@gmail.com

wowcode의 이미지

winchild wrote:
정확하게 구현되어 있는 소오스를 들여다 본것은 아니고, 운영체제마다 조금씩 다를수 있지만, 이런식의 함수를 호출해서 사용할 경우에는 일단 free 보다도 그 메모리의 데이터가 유지되어 있는것에 대하여 신경을 쓰실필요가 있습니다.

일단 함수가 호출되면, 그 데이터는 HEAP 의 임시영역에 잡힙니다. 그리고 데이터가 만들어지고, HEAP 영역의 데이터 포인터를 리턴하고 종료합니다. 즉 이미 free() 가 된것과 마찬가지의 상태입니다.

그런데 이렇게 HEAP 영역에 잡힌 데이터 영역은 그 함수의 사용이 종료되면 사실 시스템에 반환됩니다. 반환된다고 하면, 데이터가 잃어버릴 염려가 있을지도 모르겠지만, 여기서 주의해야 할 요건이 생기는 것입니다.

HEAP 영역의 메모리가 시스템에 반환 되었다고 하더라도 그 프로세서 가 종료된것은 아니므로, 그 메모리는 계속 그 프로세서가 사용할수 있습니다. 따라서 그다음의 조작에 영향을 받습니다. 다른 함수를 호출하여 그 HEAP 공간을 사용하게 되면 그 데이터는 거의 깨진다고 봐야 합니다.

그러므로 함수를 호출하기전에 데이터를 자신의 메모리 영역으로 옮겨놓는것이 필요할수 있습니다. 포인터 함수이므로 strcpy, memcpy 등으로 옮길수 있는데, 이런함수는 매크로 수준이거나, 함수가 호출되어도 HEAP 공간을 거의 사용하지 않으므로 데이터를 이용할 수 있습니다.

그러나 좀더 복잡한 함수, 예를 들면 printf 같은 것을 호출하면 이때는 거의 100% 깨집니다. 따라서 보통 printf ("%ld%, ctime()) 같은 것은 사용하면 안됩니다. 거의 세그멘테이션 폴트가 발생할것 입니다.

좀 정리가 되셨는지요.

- 겨울아찌 -


static 변수는 데이타 영역에 저장됩니다. 그리고 HEAP 영역의 변수는 함수의 사용이 종료된다고 해서 메모리가 반환되지는 않습니다. 반드시 free 해야 합니다. 스택영역과 혼돈하신 듯 합니다.

따라서,

printf("%ld",ctime());

는 Segmentation fault를 일으키지 않습니다.

akbar의 이미지

...

Viz의 이미지

지금까지 나온 내용대로라면 역시 함수 내에 static으로 잡혀 있는 것이고, 이는 메모리에서는 아마 bss 영역에 잡혀 있겠지요.

이부분은 function call과는 전혀 관계없이 process가 메모리에서 제거 될 때 까지 유지된답니다.

그럼~

My Passion for the Vision!

winchild의 이미지

ctime() 라이브러리의 함수 소오스를 찾아서 확인해 봐야 겠지요.

그 소오스내에서 리턴하는 변수부분을 static 으로 잡았으면 함수가 종료되도 메모리가 해제되지 않을것 이구요. dynamic 이면 (static 프리픽스가 없다면) 종료시에 해제되겠지요.

이것은 여러 UNIX 운영체제 프로그램을 사용해보면서 격었던 경험담에서 추측한것 이니까. 딱히 printf ("%s",ctime()); 이 종료되지 않는다고 단정할수는 없겠지요. (실제 세그멘테이션 폴트를 경험한적이 있다는 것을 말하는 것입니다.)

그리고 HEAP 과 STACK 의 구분은 어쨌든 임시공간이라는 것은 의미가 같겠지요?

- 겨울아찌 -

- 겨울아찌 -
winchild@gmail.com

익명사용자의 이미지

위에서 쓰신 말씀중에.. 호출된 함수 내부에서 할당한 HEAP의 영역은 함수가 리턴되면 자동으로 반환된다..라고 쓰셨는데..이건 제가 아는 상식으로는 말이 안되는 얘기입니다. 함수가 리턴되면서 반환되는것은 해당 함수가 호출되면서 쌓인 STACK AREA 입니다. HEAP은 절대로..자동 반환되지 않습니다. HEAP은 해당 프로세스가 종료할때까지 계속 존재하며, 명시적으로 free 하지 않으면 계속 할당된 상태로 남아 있게 됩니다. HEAP과 STACK 은 임시공간이다..라고 같이 넘어갈 성질은 전혀 아닙니다. HEAP은 임시공간이 아닙니다.

너무 오래된 글이라 답글 다는것도 민망합니다만. 다시한번 알아보시기 바랍니다.

pynoos의 이미지

구현에 따라 달라지겠지만, 전통적으로 reentrant하지 않는 형태로 구현된것은 static에,
만일 reentrant하도록 만들어졌다면, heap에 할당하고 그 주소를 thread specific data에 넣어 관리할 것입니다.

아래는 대표적인 두 OS의 libc 소스입니다. 둘다 static 입니다.
ctime은 다른 socket 계열 함수와 달리 아직은 reentrant를 지원하지 않는 군요.

Free BSD

http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdtime/localtime.c?rev=1.36&content-type=text/x-cvsweb-markup

char *
ctime(timep)
const time_t * const	timep;
{
/*
** Section 4.12.3.2 of X3.159-1989 requires that
**	The ctime funciton converts the calendar time pointed to by timer
**	to local time in the form of a string.  It is equivalent to
**		asctime(localtime(timer))
*/
	return asctime(localtime(timep));
}



struct tm *
localtime(timep)
const time_t * const	timep;
{
	static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER;
	static pthread_key_t localtime_key = -1;
	struct tm *p_tm;

	if (__isthreaded != 0) {
		_pthread_mutex_lock(&localtime_mutex);
		if (localtime_key < 0) {
			if (_pthread_key_create(&localtime_key, free) < 0) {
				_pthread_mutex_unlock(&localtime_mutex);
				return(NULL);
			}
		}
		_pthread_mutex_unlock(&localtime_mutex);
		p_tm = _pthread_getspecific(localtime_key);
		if (p_tm == NULL) {
			if ((p_tm = (struct tm *)malloc(sizeof(struct tm)))
			    == NULL)
				return(NULL);
			_pthread_setspecific(localtime_key, p_tm);
		}
		_pthread_mutex_lock(&lcl_mutex);
		tzset_basic();
		localsub(timep, 0L, p_tm);
		_pthread_mutex_unlock(&lcl_mutex);
		return(p_tm);
	} else {
		tzset_basic();
		localsub(timep, 0L, &tm);
		return(&tm);
	}
}

http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdtime/asctime.c?rev=1.11&content-type=text/x-cvsweb-markup

char *
asctime(timeptr)
const struct tm *	timeptr;
{
	static char		result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
					3 + 2 + 1 + 1];
	return(asctime_r(timeptr, result));
}

char *
asctime_r(timeptr, result)
const struct tm *	timeptr;
char *result;
{
	static const char	wday_name[][3] = {
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
	};
	static const char	mon_name[][3] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};
	/*
	** Big enough for something such as
	** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
	** (two three-character abbreviations, five strings denoting integers,
	** three explicit spaces, two explicit colons, a newline,
	** and a trailing ASCII nul).
	*/
	const char *	wn;
	const char *	mn;

	if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
		wn = "???";
	else	wn = wday_name[timeptr->tm_wday];
	if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
		mn = "???";
	else	mn = mon_name[timeptr->tm_mon];
	/*
	** The X3J11-suggested format is
	**	"%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
	** Since the .2 in 02.2d is ignored, we drop it.
	*/
	(void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
		wn, mn,
		timeptr->tm_mday, timeptr->tm_hour,
		timeptr->tm_min, timeptr->tm_sec,
		TM_YEAR_BASE + timeptr->tm_year);
	return result;
}

Linux.. glibc
http://sources.redhat.com/cgi-bin/cvsweb.cgi/libc/time/ctime.c?rev=1.5&content-type=text/x-cvsweb-markup&cvsroot=glibc

char *
ctime (const time_t *t)
{
  /* The C Standard says ctime (t) is equivalent to asctime (localtime (t)).
     In particular, ctime and asctime must yield the same pointer.  */
  return asctime (localtime (t));
}

http://sources.redhat.com/cgi-bin/cvsweb.cgi/libc/time/asctime.c?rev=1.15&content-type=text/x-cvsweb-markup&cvsroot=glibc

static const char format[] = "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n";
static char result[              3+1+ 3+1+20+1+20+1+20+1+20+1+20+1 + 1];

/* Returns a string of the form "Day Mon dd hh:mm:ss yyyy\n"
   which is the representation of TP in that form.  */
char *
asctime (const struct tm *tp)
{
  return __asctime_r (tp, result);
}
libc_hidden_def (asctime)

char *
__asctime_r (const struct tm *tp, char *buf)
{
  if (tp == NULL)
    {
      __set_errno (EINVAL);
      return NULL;
    }

  if (sprintf (buf, format,
               (tp->tm_wday < 0 || tp->tm_wday >= 7 ?
                "???" : ab_day_name (tp->tm_wday)),
               (tp->tm_mon < 0 || tp->tm_mon >= 12 ?
                "???" : ab_month_name (tp->tm_mon)),
               tp->tm_mday, tp->tm_hour, tp->tm_min,
               tp->tm_sec, 1900 + tp->tm_year) < 0)
    return NULL;

  return buf;
}
weak_alias (__asctime_r, asctime_r)
kalstein의 이미지

함수마다 달라요...;;

어떤건 static, 어떤건 heap... heap으로 되는 녀석들 같은경우 man page에서 친절하게 반드시 free를 해야된다고 나와있습니다. static인 녀석들도 나와있지요...멀티스레드 환경하에서는 static으로 되는 녀석들은 호출하기 안좋지요.


------------------------------------------
Let`s Smart Move!!
http://kalstein.tistory.com/

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.