your programing

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

lovepro 2020. 10. 9. 11:32
반응형

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


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

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)를 통해 수행 되도록 지정하므로 매우 빠르고 효율적입니다. 따라서 특정 경우에는 struct다른 작업을 수행하는 것보다 그러한 2- 스칼라 필드를 반환하는 것이 더 빠를 것입니다 (예 : 인수로 전달 된 포인터에 저장).

이러한 2- 스칼라 필드를 반환하는 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. calls the destructor ~mystruct on the temporary variable used by the compiler

Now, if mystruct is as simple as that described here all is fine, but if it has pointer (like char*) or more complicated memory management, then it all depends on how mystruct::operator=, mystruct(const mystruct&), and ~mystruct are implemented. Therefore, I suggest cautions when returning complex data structures as value.


It is perfectly safe to return a struct as you have done.

Based on this statement however: Because my understanding is that once the scope of the function is over, whatever stack was used to allocate the structure can be overwritten, I would imagine only a scenario where any of the members of the structure was dynamically allocated (malloc'ed or new'ed), in which case, without RVO, the dynamically allocated members will be destroyed and the returned copy will have a member pointing to garbage.


I will also agree with sftrabbit , Life indeed ends and stack area gets cleared up but the compiler is smart enough to ensure that all the data should be retrieved in registers or someother way.

A simple example for confirmation is given below.(taken from Mingw compiler assembly)

_func:
    push    ebp
    mov ebp, esp
    sub esp, 16
    mov eax, DWORD PTR [ebp+8]
    mov DWORD PTR [ebp-8], eax
    mov eax, DWORD PTR [ebp+12]
    mov DWORD PTR [ebp-4], eax
    mov eax, DWORD PTR [ebp-8]
    mov edx, DWORD PTR [ebp-4]
    leave
    ret

You can see that the value of b has been transmitted through edx. while the default eax contains value for a.


It is not safe to return a structure. I love to do it myself, but if someone will add a copy constructor to the returned structure later, the copy constructor will be called. This might be unexpected and can break the code. This bug is very difficult to find.

I had more elaborate answer, but moderator did not like it. So, at your expence, my tip is short.


Let's add a second part to the question: Does this vary by compiler?

Indeed it does, as I discovered to my pain: http://sourceforge.net/p/mingw-w64/mailman/message/33176880/

I was using gcc on win32 (MinGW) to call COM interfaces that returned structs. Turns out that MS does it differently to GNU and so my (gcc) program crashed with a smashed stack.

It could be that MS might have the higher ground here - but all I care about is ABI compatibility between MS and GNU for building on Windows.

If it does, then what is the behavior for the latest versions of compilers for desktops: gcc, g++ and Visual Studio

You can find some messages on a Wine mailing list about how MS seems to do it.

참고URL : https://stackoverflow.com/questions/9590827/is-it-safe-to-return-a-struct-in-c-or-c

반응형

'your programing' 카테고리의 다른 글

Angular JS 필터가 같지 않음  (0) 2020.10.09
메모장 ++ 증분 교체  (0) 2020.10.09
주어진 달의 순간 JS 시작 및 끝  (0) 2020.10.09
Pulsing Heart CSS 애니메이션  (0) 2020.10.09
ASP.NET Core 2.0 인증 미들웨어  (0) 2020.10.09