함수 내 지역변수의 생존 여부에 관해 질문드립니다.

parkyh8618의 이미지


vector<string> convert(string s, int n)
{
    vector<string> v;
    for(int i = 0 ; i <s.length() ; i+=n)
    {
        v.push_back(s.substr(i,n));
    }
    return v;
}

에서 vector v를 반환하는데 함수 밖에서 v를 쓸수가 있나요?
함수가 끝나면 v는 함수 내 지역변수니까 함수 밖에선 없어져야 하는 거 아닌가요?

함수 밖에서 쓰려면 vector를 함수 인자로 받아서 써야 될거 같은데 저렇게 써봐도 작동이 되서 질문드립니다!

익명 사용자의 이미지

Any problem?

int DeepThought(){
    const int Answer = 42;
    return Answer;
}

이 맥락에서 지역 변수는 함수를 호출한 쪽으로 그 값이 복사/이동 됩니다.

물론 본격적으로 파고 들면 더 복잡하지요. (Named) return value optimization이라고 찾아 보시면 됩니다.

익명 사용자의 이미지

cats96의 이미지

값이 남는거지 v가 남아있는게 아니죠

Stephen Kyoungwon Kim@Google의 이미지

https://shaharmike.com/cpp/rvo/
https://stackoverflow.com/questions/3106110/what-is-move-semantics

위의 링크에 잘 설명되어 있습니다.

문제의 핵심은 뭐냐면, 자바나 파이썬과 달리 C++의 user-defined class 타입의 variable은 object는 포인터나 레퍼런스가 아니라는 겁니다.

MyClass x;  // sizeof(MyClass) is 500 Mb
auto y = x;

위의 c++ code는 나이브하게 보면, x만큼, 즉 500메가 만큼 메모리를 할당한 뒤, y만큼, 그러니까 또 다른 500 메가 만큼의 메모리를 추가로 할당해야 합니다.

MyClass x = new MyClass();  // sizeof(MyClass) is 500 Mb
MyClass y = x;

위의 JAVA 코드는 다릅니다. 어딘가(HEAP)에 MyClass 객체를 딱 하나만 생성하고 x는 그 객체를 가리킬 뿐입니다. y = x 부분은 C++과 달리 그냥 x가 가리키는 객체를 y도 가리키게 할 뿐입니다. 즉 y는 x의 복사본이 아니라 x, y가 같은 객체를 가리키고 있습니다.

C++에서는 이걸 피하려면 포인터 연산을 명시적으로 사용했어야 합니다. 그런데 또 C++은 garbage collector도 없었고 해서 포인터를 사용하면 프로그래머의 생산성이나 프로그램의 유지 보수 비용에는 안 좋은 영향을 줄 수 있습니다.

vector<string> r = convert(s, n);

여전히 나이브하게 생각해 보죠. 본문의 convert 안에서는 일단 v가 생성됩니다. 대충 사이즈가 1M 라고 해보죠. int 변수가 리턴될 때는 그 값을 갖는 임시 int 객체가 만들어집니다. 마찬가지로 나이브하게 보면, v를 카피해서 임시 객체를 만들고, 그걸 리턴합니다. 그게 r에 assign이 되니까 또 카피가 일어납니다.

v가 매우 긴 string을 vector로 바꾼 거니까, 이 경우에 copy는 string에 있던 문자값 전체를 일일이 카피하는 거고 오래 걸리겠죠. 임시 객체를 만들 때 카피 한 번, r에 어사인 할 때 또 한 번 일어나니까 낭비가 심합니다. 더구나 메모리 할당 측면에서도 v, 임시 객체, r 이렇게 vector 사이즈의 세 배의 용량이 필요합니다.

RVO는 이걸 해결하는 최적화의 하나입니다. v와 임시객체를 만드는 대신, 컴파일러가 알아서 convert 코드를 고칩니다. 그래서 메모리는 r만큼만 잡고, convert에는 r의 레퍼런스/포인터를 넘겨줍니다. 리턴을 할 때는 카피하는 대신 아무 것도 안 합니다. 이러면 r만 생성하면 되고, 카피는 따로 일어나지 않죠.

문제는 vector 생성자에 side effect가 있을 수도 있다는 점입니다. RVO 최적화 전에는 생성자가 예컨대 3번 불립니다. RVO 후에는 한 번 불리고요. 그런데 생성자에 사이드 이펙트가 있다면, 뭔가가 달라져서 프로그램 최적화 전후가 다른 결과를 가져오게 됩니다. 그건 컴파일러가 일반적으로는 해서 안 될 일이죠.

하지만 C++ 스탠다드를 만든 사람도 이 중복 카피 등등이 막장인 상황임을 잘 알기 때문에 RVO에 대해서는 사이드 이펙트가 있더라도 최적화를 허용해 줍니다. 즉 언어 표준은 RVO 최적화로 인해 생성자, 소멸자 등에 들어 있는 사이드 이펙트가 프로그램을 다르게 만들어도 괜찮다고 허용해 주는 거죠.

그밖에 variable이 heap object의 reference가 아니라 variable과 같은 scope에 있는 object 그 자체라서 생기는 불필요한 카피를 줄이기 위해 move semantics라는 게 도입되었습니다. 문제는 이 move semantic을 명시적으로 리턴할 때 사용하는 경우인데, 위의 링크 첫 번째를 보시면 그게 왜 비효율적인지 설명되어 있습니다.

댓글 달기

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