C 또는 C ++로 구조체를 반환하는 것이 안전합니까? mystruct retval;

내가 이해하는 것은 이것이 수행되어서는 안된다는 것입니다.하지만 나는 이와 같은 일을하는 예제를 보았다고 믿습니다 (코드가 반드시 구문 적으로 정확하지는 않지만 아이디어가 있습니다)

typedef struct{
    int a,b;
}mystruct;

그리고 여기에 함수가 있습니다.

mystruct func(int c, int d){
    mystruct retval;
    retval.a = c;
    retval.b = d;
    return retval;
}

나는 우리가 이와 같은 일을하고 싶다면 항상 malloc 구조체에 대한 포인터를 반환해야한다는 것을 이해했지만, 이런 일을하는 예제를 본 적이 있다고 확신한다. 이 올바른지? 개인적으로 나는 항상 malloc의 구조체에 대한 포인터를 반환하거나 함수에 대한 참조로 전달하고 거기에서 값을 수정합니다. (내 이해는 함수의 범위가 끝나면 구조를 할당하는 데 사용 된 스택을 덮어 쓸 수 있다는 것입니다).

질문에 두 번째 부분을 추가해 보겠습니다. 컴파일러에 따라 다를까요? 그렇다면 데스크톱 용 최신 버전의 컴파일러 gcc, g ++ 및 Visual Studio의 동작은 무엇입니까?

문제에 대한 생각?



답변

완벽하게 안전하며 그렇게하는 것은 잘못이 아닙니다. 또한 컴파일러에 따라 다르지 않습니다.

일반적으로 (예와 같이) 구조체가 너무 크지 않은 경우이 접근 방식이 malloc 구조를 반환하는 것보다 훨씬 낫다고 주장합니다 ( malloc비용이 많이 드는 작업입니다).


답변

완벽하게 안전합니다.

당신은 가치로 돌아오고 있습니다. 정의되지 않은 동작으로 이어지는 것은 참조로 반환하는 경우입니다.

//safe
mystruct func(int c, int d){
    mystruct retval;
    retval.a = c;
    retval.b = d;
    return retval;
}

//undefined behavior
mystruct& func(int c, int d){
    mystruct retval;
    retval.a = c;
    retval.b = d;
    return retval;
}

스 니펫의 동작은 완벽하게 유효하고 정의되어 있습니다. 컴파일러에 따라 다르지 않습니다. 괜찮아!

개인적으로 나는 항상 malloc 구조에 대한 포인터를 반환합니다.

해서는 안됩니다. 가능하면 동적으로 할당 된 메모리를 피해야합니다.

또는 함수에 대한 참조로 전달하고 값을 수정하십시오.

이 옵션은 완벽하게 유효합니다. 선택의 문제입니다. 일반적으로 원래 구조체를 수정하는 동안 함수에서 다른 것을 반환하려면이 작업을 수행합니다.

내 이해는 함수의 범위가 끝나면 구조를 할당하는 데 사용 된 스택을 덮어 쓸 수 있다는 것입니다.

이것은 잘못된 것입니다. 내 말은, 일종의 정확하지만 함수 내부에서 생성 한 구조의 복사본을 반환합니다. 이론적으로 . 실제로 RVO 는 발생할 수 있으며 발생할 수 있습니다. 반환 값 최적화에 대해 읽어보십시오. 즉 retval, 함수가 종료 될 때 범위를 벗어나는 것처럼 보이지만 실제로는 추가 복사를 방지하기 위해 호출 컨텍스트에 빌드 될 수 있습니다. 이것은 컴파일러가 자유롭게 구현할 수있는 최적화입니다.


답변

mystruct함수 에서 객체 의 수명 은 실제로 함수를 떠날 때 끝납니다. 그러나 return 문에서 값으로 개체를 전달합니다. 이것은 객체가 함수에서 호출 함수로 복사됨을 의미합니다. 원본 개체는 사라지지만 복사본은 계속 유지됩니다.


답변

structC에서 a를 반환하는 것이 안전 할뿐만 아니라 (또는 classC ++에서 struct-s는 실제로 class기본 public:멤버 가있는 -es 임), 많은 소프트웨어가이를 수행합니다.

물론 classC ++에서 a 를 반환 할 때 언어는 일부 소멸자 또는 이동 생성자가 호출되도록 지정하지만 컴파일러에 의해 최적화 될 수있는 경우가 많습니다.

또한 Linux x86-64 ABIstruct두 개의 스칼라 (예 : 포인터 또는 long) 값 으로 a를 반환하는 작업이 레지스터 ( %rax& %rdx)를 통해 수행 되도록 지정하므로 매우 빠르고 효율적입니다. 따라서이 특별한 경우에는 이러한 2- 스칼라 필드를 반환하는 것이 더 빠를 것입니다.struct 다른 작업을 수행하는 것보다 것입니다 (예 : 인수로 전달 된 포인터에 저장).

그런 두 스칼라 필드를 반환하는 struct것은 malloc-ing하고 포인터를 반환하는 것보다 훨씬 빠릅니다 .


답변

그것은 완벽하게 합법적이지만 큰 구조체의 경우 고려해야 할 두 가지 요소, 즉 속도와 스택 크기가 있습니다.


답변

구조 유형은 함수가 반환하는 값의 유형일 수 있습니다. 컴파일러가 구조체의 복사본을 만들고 함수의 로컬 구조체가 아닌 복사본을 반환하기 때문에 안전합니다.

typedef struct{
    int a,b;
}mystruct;

mystruct func(int c, int d){
    mystruct retval;
    cout << "func:" <<&retval<< endl;
    retval.a = c;
    retval.b = d;
    return retval;
}

int main()
{
    cout << "main:" <<&(func(1,2))<< endl;


    system("pause");
}

답변

안전성은 구조체 자체가 어떻게 구현되었는지에 달려 있습니다. 비슷한 것을 구현하는 동안이 질문에 우연히 발견되었으며 여기에 잠재적 인 문제가 있습니다.

컴파일러는 값을 반환 할 때 몇 가지 작업을 수행합니다.

  1. 복사 생성자를 호출합니다 mystruct(const mystruct&)( 컴파일러 자체에 의해 할당 된 함수 외부this임시 변수 ).func
  2. ~mystruct내부에 할당 된 변수에 대해 소멸자 를 호출합니다.func
  3. mystruct::operator=반환 된 값이 다른 것에 할당되면 호출 합니다.=
  4. ~mystruct컴파일러가 사용하는 임시 변수 에서 소멸자 를 호출합니다.

이제 경우가 mystruct여기에 설명 된 것과 간단하다 모든 괜찮지 만, 그것은 포인터 (같은 경우 char*) 또는 더 복잡한 메모리 관리를, 다음 모든 방법에 따라 달라집니다 mystruct::operator=, mystruct(const mystruct&)그리고 ~mystruct구현됩니다. 따라서 복잡한 데이터 구조를 값으로 반환 할 때주의해야합니다.