char * 를 리턴해주는 함수

linuxqna의 이미지

먼저 간단한 예제를 작성해보죠

char *getName() {
    char name[] = "KLDP";
    return name;
}

int main(void) {
    char n = getName();
    return 0;
}

아시다시피 이렇게 하면
local variable(즉 함수끝에서 메모리에서 소멸될) 을 return 하기 때문에
에러가 나게 됩니다.
즉 이런상황에서... char * 를 넘겨주는 함수는 보통 어떻게 작성되는것이
일반적인 방법론인가요 ?

제가 생각한거는

char * getName(char *str) {
    strcpy(str, "KLDP");
    return str;
}

이렇게 해도 될것같고 (물론 length체크루틴을 넣으면 더 좋겠고)
일반적인 char * 를 return해주는 함수에서의
best way를 보여주세요.
익명 사용자의 이미지

char *getName(blah...)
{
  char *str;
  str = malloc(5);
  strcpy(str, "KLDP");
  return str;
}
yykim의 이미지

char name[] 을 static char name[] 으로 바꿔도 ...

dummy999의 이미지

char name[] 을 static char name[] 으로 바꿔도 ...
static으로 바꿀만한 가치가 있다면. 바꾸는게 좋겠죠. 그러나 대부분 static쓸려면
전역변수가 바람직하지않을까합니다.

동적 메모리할당은.. 좀어렵구..
심플리티하게 linuxqna님의 처음문장을 재구성했습니다.

char *getName(void)
{
char *name = "KLDP";
return name;
}

int main(void) {
char *n;
n= getName();
printf("%s\n",n);
return 0;
}
:oops: :D :) :wink: :roll:

------------------------------------
F/OSS bless you... ^^*

linuxqna의 이미지

먼저

1. 함수안에서

char *name = "KLDP";
char name[] = "KLDP";

가 어떻게 다른지 ? 제가 이해하기로는

name[] 은 KLDP를 위한 메모리가 할당되고 바로 그 주소를 가리키지만
return을 해도 그 메모리는 함수종료시 메모리 반환이 되므로 warning이고

*name 은 메모리 어딘가에 "KLDP" 를 할당하고 그 시작점을 가리키는
포인터를 선언하고 그 주소를 넘겨주므로
(어딘가에 선언된 "KLDP"는 함수가 끝나도 유효)
함수가 종료하면서 *name이 반환되더라도 이미 return값으로
그 주소를 넘겼으므로 참조할수 있다

인데 맞나요 ?
그렇다면 어딘가에 선언된 "KLDP"는 언제 메모리가 반환되나요 ?
프로그램 종료시 ?

그리고 방준영님의 방법론에서 dynamic allocation에서는
언제 alloc된 메모리를 free시켜야 하나요 ?
프로그래머가 더이상 필요가 없다고 판단되는 시점/루틴에서
free 시키면 되나요 ?
free를 따로 안시키면 프로그램 종료시에 자동 소멸되고..

감사합니다.

맹고이의 이미지

제가 알기로는..

char *name = "KLDP"; 
char name[] = "KLDP"; 

는 머.. 그냥 같은걸로 알고있습니다;;

근데 char *name = "KLDP" 는
constant character array 로 알고 있거든요
그래서 문자열을 수정하지 않을때 이렇게 선언하구요..

char name[] = "KLDP"는
문자열을 수정해야 한다던지 할때 사용합니다..
제가 그렇다는거구요-_- 아마 차이가 없을듯하네용

결국 그냥 선언했으면
함수가 끝나면 둘다 없어지는건 마찬가지 아닐까요-_-

그리고 동적할당을 했으면
프로그램이 끝나기 전에 해제 시켜야지요;;

charsyam의 이미지

linuxqna wrote:
먼저

1. 함수안에서

char *name = "KLDP";
char name[] = "KLDP";

가 어떻게 다른지 ? 제가 이해하기로는

name[] 은 KLDP를 위한 메모리가 할당되고 바로 그 주소를 가리키지만
return을 해도 그 메모리는 함수종료시 메모리 반환이 되므로 warning이고

*name 은 메모리 어딘가에 "KLDP" 를 할당하고 그 시작점을 가리키는
포인터를 선언하고 그 주소를 넘겨주므로
(어딘가에 선언된 "KLDP"는 함수가 끝나도 유효)
함수가 종료하면서 *name이 반환되더라도 이미 return값으로
그 주소를 넘겼으므로 참조할수 있다

인데 맞나요 ?
그렇다면 어딘가에 선언된 "KLDP"는 언제 메모리가 반환되나요 ?
프로그램 종료시 ?

그리고 방준영님의 방법론에서 dynamic allocation에서는
언제 alloc된 메모리를 free시켜야 하나요 ?
프로그래머가 더이상 필요가 없다고 판단되는 시점/루틴에서
free 시키면 되나요 ?
free를 따로 안시키면 프로그램 종료시에 자동 소멸되고..

감사합니다.

char *name = "KLDP";
char name[] = "KLDP" 는 차이가 있습니다.

char *name = "KLDP" 의 경우 KLDP 라는 문자열은 data 영역에 잡히고
name 은 bss 에 잡혀서 단순히 name 변수가 KLDP를 포인팅 합니다.

하지만 char name[] = "KLDP" 에서는, KLDP 라는 문자열은 data 영역에
잡히고 name은 bss에 잡히더라도, name이 "KLDP"를 포인팅 하는게
아니라, 중간에 name이 배열이므로, KLDP를 복사하는 루틴이 들어가게 됩니다. 그래서 첫번째 것의 값을 바꾸려고 하면, 에러가 나게 되지만, 두번째
것은 자신의 메모리를 가지고 있으므로, 그냥 값이 바꿔 집니다.

어셈블을 해보면, 복사 루틴이 있는 것을 볼 수 있습니다.

=========================
CharSyam ^^ --- 고운 하루
=========================

charsyam의 이미지

아, 그리고 동적 할당은 중간에 필요없다면 바로 지워줘야 겠죠. 그런데

포인터를 계속 유지 못하면, 메모리가 낭비됩니다.

저 같은면, 동적 할당 한것들은 전부 리스트에 넣어두고, 필요없을 때

마다, 그 리스트에 든 것들을 쫙 지우는게 좋을듯하네요. ^^

고운 하루

=========================
CharSyam ^^ --- 고운 하루
=========================

june8th의 이미지

저라면 이렇게 작성하겠습니다.

char * getName( char * buf, int buf_len ) {

    // name을 얻는다.
    buf[buf_len] = '\0';
    return strncpy( buf, name, buf_len-1 )
}

// 사용예
char name[MAX_NAME_LEN+1];
printf( "name : %s\n", getName( name, sizeof(name) ) );

moonzoo의 이미지

Quote:

char *name = "KLDP";
char name[] = "KLDP" 는 차이가 있습니다.

char *name = "KLDP" 의 경우 KLDP 라는 문자열은 data 영역에 잡히고
name 은 bss 에 잡혀서 단순히 name 변수가 KLDP를 포인팅 합니다.

하지만 char name[] = "KLDP" 에서는, KLDP 라는 문자열은 data 영역에
잡히고 name은 bss에 잡히더라도, name이 "KLDP"를 포인팅 하는게
아니라, 중간에 name이 배열이므로, KLDP를 복사하는 루틴이 들어가게 됩니다. 그래서 첫번째 것의 값을 바꾸려고 하면, 에러가 나게 되지만, 두번째
것은 자신의 메모리를 가지고 있으므로, 그냥 값이 바꿔 집니다.

어셈블을 해보면, 복사 루틴이 있는 것을 볼 수 있습니다.

그러나 함수내에서 다음과 같은 두 문장은
char *name = "KLDP";
char name[] = "KLDP"

결국 둘다 name을 리턴할 경우 보장할 수 없는 값을

받게 되죠..

dummy999의 이미지

char *name = "KLDP";
char name[] = "KLDP"
이것의 차이는 말씀하신대로 메모리상의 임의의장소에 자리를 잡는건가 아니면
합법적인 장소에 자리를 잡는가의 차이입니다.

그런데 둘다 동일하다는 의미는 둘다 정적할당이기 때문이죠.
다시말해 플그램이 종료되거나 함수종료되면. 확! 지워지는걸로 알고있습니다.
동적할당은 그것들이 다수개가 될수있기때문에 비신뢰적이지만.
정적할당의 경우엔 오직 하나가 메모리하나를 관리하므로 제거될때 자동으로 제거되지
않을까요? ^^;

만약 동적할당의 경우엔 내가 메모리할당해놓고 할당번지를 잊어버리면
컴이 리부팅전까지는 항상 그번지는 누구도 손을 못대게 하지만.(의미상으로 보호받게되졍)
편법을 써가면 그번지는 쓸수있지만. 그러나 실질적인보호는 메모리 오버라이트가 되죠..

ㅎㅎㅎ 한번 해보시고 결과점부탁드립니다.(제가 586쓰기때문에.. ^^;)

------------------------------------
F/OSS bless you... ^^*

charsyam의 이미지

보기엔, 딱 한번만 쓴다거나, 그런 것이 아니라면

strcpy 를 이용해서

char buffer[128];
GetName( buffer );

char *GetName( char *buffer )
{
strcpy( buffer, "KLDP" );
return buffer;
}

가 낳을듯 합니다. ^^ 그럼 고운 하루

=========================
CharSyam ^^ --- 고운 하루
=========================

vacancy의 이미지

방준영님의 코드가 제일 적절하다는 생각이 드네요.

buelgsk8er의 이미지

아닙니다.

char name[] = "KLDP";
return name;

char *name = "KLDP";
return name;

전자는 함수가 종료하면 invalid 한 영역 - 즉 스택 -에 대한 포인터를 반환하지만
후자는 함수가 종료한 후에도 valid한 영역 - 데이터 섹션이 되었건 코드 섹션이 되었건 간에 - 에 대한 포인터를 반환합니다.

따라서 전자-스택에 잡힌 배열을 반환하는 방법-은 잘못된 방법이지만 후자-문자열상수-를 반환하는 것은 문제 없습니다.

다만 후자의 단점은 말그대로 문자열 상수만 반환가능하다는 것입니다. 즉 위의 예처럼 "KLDP" 같은 상수를 반환하는 경우라면 별 문제가 없지만, 만약 _snprintf등을 사용해서 런타임에 문자열을 합성해야 할 경우라면 위의 분들이 보여주신 것처럼 malloc을 통한 동적할당을 하던지 아니면 인자를 통해 복사받을 버퍼를 넘겨받던지, 아니면 static array를 사용해야겠지요. 각 방법의 장단점을 살펴보면
1. 복사받을 버퍼 및 버퍼크기를 인자로 넘겨받는다.
--> 사용하기도 구현하기도 귀찮다-_-; 하지만 가장 표준적/범용적이고 안전함
2. 동적할당
--> free를 잊지말고 해주어야 한다는 귀찮음보다는, 1번이라면 필요없을 "malloc"의 오버헤드가 덧붙여진다는 점이 가장 큰 단점임.
3. static array
--> 간편하지만 함수가 reentrant하지 않게 된다는 단점이 있음. 즉 서로 다른 쓰레드에서 접근한다거나 signal handler등에서 사용할 수 없음

__일반적으로__ 가장 추천할 방법은 역시 귀찮긴 해도 1번인 것 같습니다(june8th님의 코드 참조)

멀요의 이미지

물론, 상황에 따라 다르겠지만,

기존 C 함수들에 보면, 그런 함수들이 많이 있는데, 소스를 보면

static으로 되있는게 많더군요.

dummy999의 이미지

저는 다르게 생각합니다. 구태여 한문자를 선언할꺼라면. 좀더 메모리 효율적으로 선언하고 그랬겠지않았을까합니다.
그러나 뭔가 함수로 만들고 또 그렇게 처리하게한다는것은 다용성을 위한 설계가 아니었을까요?
다시말해 배열로서 잡아진 위의 방법들은 1회용이라는말밖에 할수없습니다.
물론 설계에따라 그안정성은 개발자가 직접적으로 해주는것이라 생각하지만

그렇지만 배열명[배열크기] = "문자열' 이런방법은 확장성자체를 무너뜨리게 되는거라 생각됩니다.

차라리 좀더안정적인 변수명* = "문자열" 이방법은 어떻습니까?
최소한 개발자라면. 그정도의 확장성을 고려하는것은 당연한것이고
또한 그상황에서 어떠한 설계를 하던간에 좀더 유동적인 설계가 될수있다고 생각합니다.

최적화를 하면할수록 메모리의 효율성이나 성능면에서 상당한 이득을 보지만
그것은 어디까지나 확장성을 전혀 고려하지않는방법이라 생각됩니다.
뭐 개발자맘이겠지만. :roll:

------------------------------------
F/OSS bless you... ^^*

kslee80의 이미지

좀 관련이 없을수도 있지만,,,
glibc 에서 char * 를 리턴하는 함수들은
대부분이 함수 나름의 static 한 공간을 가지고
그 공간의 포인터를 리턴하게 작성되어 있습니다..
먼저 답변하신분 말씀처럼, reentrant 하지 않죠..

그래서 같은 기능을 하는 함수여도 reentrant 하게 작성한
이름이 다른 함수가 존재하구요..(보통 *_rr 이라는 이름을 가지죠)
이런 이름이 다른 함수들은 아규먼트로 char * 변수를 받죠..

저의 경우에는, error message 함수같은 값을 고칠 필요가 없는 경우에는
return "error message";
같은 방식을 쓰지만, 그외의 경우에는 모두다 아규먼트를 받게 처리합니다..

cjy1126의 이미지

char *str = "hi";     //static 영역에 저장
char str[] = "hi";    //stack 영역에 저장
char *str;
str = (char *)malloc(sizeof(char)*2);
str = "hi";              //heap 영역에 저장

이걸로 기억이... 가물... 가물... 하네요.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.