구조물을 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's structure로 되돌려야 한다는 것은 이해했지만, 이런 일을 하는 예를 본 적이 있다고 확신합니다.이거 맞는건가요?개인적으로 저는 항상 포인터를 malloc's structure로 되돌리거나 함수에 대한 참조를 통해 pass by function을 수행하고 값을 변경합니다(함수의 범위가 끝나면 구조 할당에 사용된 스택은 덮어쓸 수 있기 때문입니다).
질문에 두 번째 부분을 추가해 보겠습니다.이것은 컴파일러에 따라 다른가요?그렇다면 데스크톱용 컴파일러의 최신 버전인 gcc, g++ 및 Visual Studio의 동작은 어떻게 됩니까?
그 문제에 대한 생각은?
완벽하게 안전하며, 그렇게 하는 것이 잘못된 것은 아닙니다.또, 컴파일러에 의해서도 다릅니다.
않을 이 ed 더합니다.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's structure에 반환한다.
그러면 안 돼요.가능하면 동적으로 메모리를 할당하지 않도록 해야 합니다.
또는 함수를 참조하고 값을 수정합니다.
이 옵션은 완전히 유효합니다.그건 선택의 문제이다.일반적으로 원래 구조를 수정하면서 함수에서 다른 항목을 반환하려면 이 작업을 수행합니다.
함수의 범위가 끝나면 구조를 할당하기 위해 사용된 스택을 덮어쓸 수 있습니다.
이는 잘못되었다.제 말은, 그건 맞는 말이지만, 함수 안에서 만든 구조의 복사본을 돌려주잖아요.이론상으로는.실제로는 RVO가 발생할 수 있으며 발생할 수 있습니다.반환값 최적화에 대해 자세히 알아보십시오.이것은 비록retval
이 함수가 종료되면, 실제로는, 추가 카피를 방지하기 위해서, 발신측 컨텍스트에 짜넣어져 있는 경우가 있습니다.이는 컴파일러가 자유롭게 구현할 수 있는 최적화입니다.
주의: 이 답변은 c++11 이후에만 적용됩니다."C/C++"는 없습니다.그것들은 다른 언어입니다.
아니요. 로컬 개체를 값으로 반환하는 것은 위험하지 않으며 반환하는 것이 좋습니다.하지만 여기 모든 답변에서 빠진 중요한 점이 있다고 생각합니다.다른 많은 사람들은 구조물이 복사되거나 RVO를 사용하여 직접 배치되고 있다고 말했다.그러나 이것은 완전히 옳지 않다.저는 현지 물건을 반품할 때 어떤 일이 일어날 수 있는지 정확히 설명하겠습니다.
의미론 이동
c++11 이후 안전하게 도난당할 수 있는 임시 객체에 대한 참조인 rvalue 참조가 있습니다.예를 들어 std::vector에는 이동 생성자 및 이동 할당 연산자가 있습니다.이 두 가지는 항상 복잡하며 단순히 포인터를 이동하는 벡터의 데이터에 복사합니다.이동 의미론에 대해서는 여기서 자세히 설명하지 않겠습니다.
함수 내에서 로컬로 작성된 객체는 일시적이며 함수가 반환될 때 범위를 벗어나기 때문에 반환된 객체는 c++11 이후로는 복사되지 않습니다.반환되는 개체에서 이동 생성자를 호출하고 있습니다(또는 나중에 설명함).즉, 큰 벡터처럼 비싼 복사 생성자를 사용하여 저렴한 이동 생성자를 반환해야 하는 경우 데이터의 소유권만 로컬 개체에서 반환된 개체로 이전되므로 비용이 저렴합니다.
이 예에서는 오브젝트를 복사하고 이동해도 차이가 없다는 점에 유의하십시오.구조체의 기본 이동 및 복사 생성자는 두 정수를 복사하는 동일한 작업을 수행합니다.그러나 전체 구조가 64비트 CPU 레지스터에 맞기 때문에 다른 솔루션보다 빠릅니다(잘못했다면 CPU 레지스터를 잘 모릅니다).
RVO 및 NRVO
RVO는 Return Value Optimization을 의미하며 컴파일러가 실행하는 몇 안 되는 최적화 중 하나로 부작용이 있습니다.c++17이므로 RVO가 필요합니다.이름 없는 오브젝트를 반환할 경우 발신자가 반환된 값을 할당하는 위치에 직접 생성됩니다.복사 생성자도 이동 생성자도 호출되지 않습니다.RVO가 없는 경우 이름 없는 객체는 먼저 로컬로 구축되고 다음으로 반환된 주소로 구성된 이동 후 로컬 이름 없는 객체가 파괴됩니다.
RVO가 필요한 경우(c++17) 또는 가능성이 높은 경우(c++17 이전):
auto function(int a, int b) -> MyStruct {
// ...
return MyStruct{a, b};
}
NRVO는 Named Return Value Optimization을 의미하며 착신 함수에 로컬인 이름 있는 오브젝트에 대해 실행되는 것을 제외하고 RVO와 동일합니다.이것은 표준(c++20)에서는 보증되지 않지만, 많은 컴파일러가 여전히 보증하고 있습니다.이름이 지정된 로컬 개체가 있더라도 반환될 때 가장 심하게 이동됩니다.
결론
값으로 반환하지 않는 것을 고려하는 유일한 경우는 이름이 붙은 매우 큰(스택크기와 같은) 개체가 있는 경우입니다.이는 NRVO가 아직 보증되지 않아(c++20 현재), 오브젝트의 이동도 느리기 때문입니다.Cpp 코어 가이드라인의 권장사항은 항상 값(복수 반환값이 여러 개인 경우 구조체(또는 태플)를 사용)에 따라 객체를 반환하는 것을 선호합니다.단, 유일한 예외는 객체의 이동에 비용이 많이 드는 경우입니다.이 경우 일정하지 않은 기준 매개변수를 사용하십시오.
c++의 함수에서 수동으로 해제해야 하는 리소스를 반환하는 것은 결코 좋은 생각이 아닙니다.절대 그러지 마세요.적어도 std::unique_ptr을 사용하거나 리소스(RAII)를 해제하는 소멸자를 사용하여 자체 비로컬 또는 로컬 구조를 만들고 그 인스턴스를 반환합니다.리소스가 자체 이동 의미를 가지고 있지 않은 경우 이동 생성자와 이동 할당 연산자를 정의하는 것도 좋은 방법입니다(및 복사 생성자/할당 삭제).
만 아니라 반환해도 합니다.struct
C( a a))에서class
에서는, C++ 에서는, 「」struct
-s입니다.class
-es)public:
많은 소프트웨어에서 그렇게 하고 있습니다.
★★★★★★★★★★★★★★★★★★★★★★★★★★★.class
C++에서, 언어는 어떤 파괴자 또는 움직이는 생성자를 호출하는 것을 지정하지만, 컴파일러에 의해 이것이 최적화될 수 있는 많은 경우들이 있다.
또, Linux x86-64 ABI 에서는, 다음과 같이 규정되어 있습니다.struct
두 개의 스칼라(예: 포인터 또는long
은 레지스터를 .%rax
&%rdx
이치노 이 의 스칼라 수 .struct
(예를 들어 인수로 전달된 포인터에 저장) 다른 작업을 수행하는 것보다 더 중요합니다.
두 의 스칼라 하다.struct
, 「」보다 .malloc
- 포인터 - 포인터 돌려주고. - 포인터 돌려주고.
또한 sftrabbit에는 동의합니다.라이프가 실제로 종료되고 스택 영역이 정리되지만 컴파일러는 모든 데이터를 레지스터 또는 다른 방법으로 불러올 수 있을 만큼 스마트합니다.
확인의 간단한 예를 다음에 나타냅니다.(Mingw 컴파일러 어셈블리에서 발췌).
_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
기본 eax에는 a 값이 포함되어 있는 반면 b 값은 edx를 통해 전송되었음을 알 수 있습니다.
의 mystruct
함수 내의 객체는 함수에서 벗어날 때 실제로 종료됩니다.하고 있습니다.이는 객체가 함수에서 호출 함수로 복사됨을 의미합니다.원래 객체는 사라졌지만 복사본은 계속 유지됩니다.
완전히 합법적이지만 대형 구조에서는 속도와 스택 크기라는 두 가지 요소를 고려해야 합니다.
구조 유형은 함수에 의해 반환되는 값의 유형일 수 있습니다.컴파일러는 구조의 복사본을 만들고 함수 내의 로컬 구조가 아닌 복사본을 반환하기 때문에 안전합니다.
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");
}
안전성은 구조물 자체의 구현 방법에 따라 달라집니다.비슷한 것을 실장하고 있을 때 우연히 이 질문을 하게 되었는데, 이것이 잠재적인 문제입니다.
컴파일러는 값을 반환할 때 다음과 같은 몇 가지 작업을 수행합니다.
- 컨스트럭터를 합니다.
mystruct(const mystruct&)
)this
함수 외부에 있는 임시 변수입니다.func
컴파일러 자체에 의해 할당됩니다.) - 를
~mystruct
되어 있는func
- ®
mystruct::operator=
이 다른 되어 있는=
- 를
~mystruct
하는 에 대해
만약 ,, 약mystruct
: '포인터'가 있는 경우:char*
하느냐에 mystruct::operator=
,mystruct(const mystruct&)
, , , , 입니다.~mystruct
구현되어 있습니다.따라서 복잡한 데이터 구조를 값으로 반환할 때 주의할 것을 제안합니다.
당신이 했던 대로 구조물을 돌려주는 것이 완벽하게 안전합니다.
단, 이 스테이트먼트에 근거하고 있습니다.함수의 범위가 끝나면 어떤 스택을 사용하여 구조체를 할당했든 덮어쓸 수 있기 때문에 구조체의 멤버 중 하나가 동적으로 할당(malloc 또는 new'ed)된 시나리오만 생각할 수 있습니다.이 시나리오에서는 RVO가 없으면 동적으로 할당됩니다.ed 멤버는 파기되고 반환된 복사본에는 가비지를 가리키는 멤버가 포함됩니다.
구조물을 반환하는 것은 안전하지 않다.제가 직접 하고 싶지만 나중에 누군가가 반환된 구조에 복사 생성자를 추가하면 복사 생성자가 호출됩니다.이것은 예기치 않은 일이며, 코드를 해독할 수 있습니다.이 버그는 찾기가 매우 어렵다.
좀 더 자세한 답변을 드렸는데 진행자가 싫어했어요.그래서 당신이 만료되면 제 팁이 모자라요.
질문에 두 번째 부분을 추가해 보겠습니다.이것은 컴파일러에 따라 다른가요?
확실히 그렇습니다.제가 깨달은 바로는 http://sourceforge.net/p/mingw-w64/mailman/message/33176880/입니다.
win32(MinGW)에서 gcc를 사용하여 구조를 반환한 COM 인터페이스를 호출하고 있었습니다.MS는 GNU와는 다르게 실행하므로 스택이 파손된 상태로 (gcc) 프로그램이 크래시됩니다.
여기서 MS가 우위에 있을 수도 있지만, 내가 신경 쓰는 것은 MS와 GNU 사이의 ABI 호환성이에요.
그렇다면 데스크톱용 컴파일러의 최신 버전인 gcc, g++ 및 Visual Studio의 동작은 어떻게 됩니까?
Wine 메일링 리스트에서 MS의 대처 방법에 대한 메시지를 찾을 수 있습니다.
언급URL : https://stackoverflow.com/questions/9590827/is-it-safe-to-return-a-struct-in-c-or-c
'programing' 카테고리의 다른 글
C의 칠드 연산자 (0) | 2022.05.28 |
---|---|
왜 'int'는 C++에서는 정상적으로 컴파일되지만 C++에서는 컴파일되지 않는가? (0) | 2022.05.28 |
변수를 기반으로 Vue 필터 선택 (0) | 2022.05.27 |
Vuex 맵은 TypeScript를 사용하여 기능합니다. (0) | 2022.05.27 |
vuejs의 v-if와 유사한 사용자 지정 지시문 (0) | 2022.05.27 |