멤버가 초기화되지 않은 일부 구조체를 복사하는 것이 유효합니까?
나는 그것이 정의되지 않은 행동이라고 생각하지만, 그렇다면 초기화되지 않은 멤버를 구조체에 남겨 두는 것은 매우 위험합니다. 그래서 표준에 허용되는 것이 있는지 궁금합니다.
예를 들어, 이것이 유효합니까?
struct Data {
int a, b;
};
int main() {
Data data;
data.a = 5;
Data data2 = data;
}
멤버가 초기화되지 않은 일부 구조체를 복사하는 것이 유효합니까?
나는 그것이 정의되지 않은 행동이라고 생각하지만, 그렇다면 초기화되지 않은 멤버를 구조체에 남겨 두는 것은 매우 위험합니다. 그래서 표준에 허용되는 것이 있는지 궁금합니다.
예를 들어, 이것이 유효합니까?
struct Data {
int a, b;
};
int main() {
Data data;
data.a = 5;
Data data2 = data;
}
답변:
예, 초기화되지 않은 멤버가 서명되지 않은 좁은 문자 유형이 아니거나 std::byte
암시 적으로 정의 된 복사 생성자를 사용하여이 결정되지 않은 값을 포함하는 구조체를 복사하는 것은 기술적으로 정의되지 않은 동작입니다. 의 [dcl.init] / 12 .
암시 적으로 생성 된 복사 생성자가 union
직접 초기화에 의한 것처럼 각 멤버를 개별적으로 복사하도록 정의 된 s를 제외하고 여기에 적용됩니다 . [class.copy.ctor] / 4를 참조하십시오 .
이것은 또한 유효한 CWG 문제 2264 의 주제입니다 .
실제로 실제로는 아무런 문제가 없을 것이라고 생각합니다.
100 % 확실 하게하려면 멤버가 값을 결정할 수없는 경우에도 std::memcpy
유형을 간단하게 복사 할 수있는 경우 항상를 사용하여 올바르게 정의 된 동작을 갖습니다.
이러한 문제를 제외하고는 클래스가 간단한 기본 생성자 를 가질 필요가 없다고 가정 할 때 항상 구성시 지정된 값으로 클래스 멤버를 올바르게 초기화해야합니다 . 기본 멤버 이니셜 라이저 구문을 사용하여 멤버를 값 초기화하는 등의 작업을 쉽게 수행 할 수 있습니다.
struct Data {
int a{}, b{};
};
int main() {
Data data;
data.a = 5;
Data data2 = data;
}
memcpy
사소한 복사 가능한 유형의 경우 에도 객체 표현을 by처럼 복사하도록 정의되지 않았습니다 . 유일한 예외는 암시 적 복사 생성자가 객체 표현을 마치 by로 복사하는 공용체 memcpy
입니다.
일반적으로 초기화되지 않은 데이터를 복사하는 것은 정의되지 않은 동작입니다. 그 데이터는 트래핑 상태 일 수 있기 때문입니다. 이 페이지 인용 :
객체 표현이 객체 유형의 값을 나타내지 않으면이를 트랩 표현이라고합니다. 문자 유형의 lvalue 표현식을 통해 읽는 것 이외의 방법으로 트랩 표현에 액세스하는 것은 정의되지 않은 동작입니다.
부동 소수점 유형의 경우 신호 NaN이 가능하며 일부 플랫폼에서는 정수에 트랩 표현 이있을 수 있습니다 .
그러나 사소하게 복사 가능한 유형 memcpy
의 경우 객체의 원시 표현을 복사하는 데 사용할 수 있습니다. 객체의 값이 해석되지 않고 대신 객체 표현의 원시 바이트 시퀀스가 복사되므로 안전합니다.
unsigned char[64]
)? 구조체의 바이트를 지정되지 않은 값을 갖는 것으로 취급하면 최적화를 불필요하게 방해 할 수 있지만 프로그래머가 불필요한 값으로 배열을 수동으로 채우도록 요구하면 효율성이 훨씬 더 떨어집니다.
설명 된 것과 같은 일부 경우에 C ++ 표준을 사용하면 컴파일러가 동작을 예측할 필요없이 고객이 가장 유용하다고 생각하는 방식으로 구문을 처리 할 수 있습니다. 다시 말해, 이러한 구성은 "정의되지 않은 동작"을 불러옵니다. 그러나 C ++ 표준이 잘 구성된 프로그램이 "허용"하는 것에 대한 관할권을 명시 적으로 포기하기 때문에 그러한 구성이 "금지"되어야 함을 의미하지는 않습니다. C ++ 표준에 대해 게시 된 Rational 문서를 알지 못하지만 C89와 같이 정의되지 않은 동작을 설명한다는 사실은 의도 된 의미가 비슷하다는 것을 시사합니다. 진단합니다.
무언가를 처리하는 가장 효율적인 방법은 다운 스트림 코드가 신경 쓰지 않는 구조 부분을 작성하는 반면, 다운 스트림 코드는 신경 쓰지 않는 부분을 생략하는 상황이 많이 있습니다. 프로그램이 신경 쓰지 않는 것을 포함하여 구조의 모든 멤버를 초기화하도록 요구하면 효율성을 불필요하게 방해 할 수 있습니다.
또한 초기화되지 않은 데이터가 비 결정적 방식으로 동작하는 것이 가장 효율적인 상황이 있습니다. 예를 들면 다음과 같습니다.
struct q { unsigned char dat[256]; } x,y;
void test(unsigned char *arr, int n)
{
q temp;
for (int i=0; i<n; i++)
temp.dat[arr[i]] = i;
x=temp;
y=temp;
}
다운 스트림 코드의 어떤 요소의 값을 걱정하지 않을 경우 x.dat
또는 y.dat
그 인덱스에 나열되지 않은 arr
, 코드가에 최적화 될 수 있습니다
void test(unsigned char *arr, int n)
{
q temp;
for (int i=0; i<n; i++)
{
int it = arr[i];
x.dat[index] = i;
y.dat[index] = i;
}
}
프로그래머가 temp.dat
복사하기 전에 다운 스트림이 신경 쓰지 않는 요소를 포함하여 의 모든 요소를 명시 적으로 작성해야하는 경우 효율성의 향상은 불가능 합니다.
반면에 데이터 유출 가능성을 피하는 것이 중요한 일부 응용 프로그램이 있습니다. 이러한 응용 프로그램에서는 다운 스트림 코드가 확인되는지 여부에 관계없이 초기화되지 않은 저장소를 복사하려는 시도를 포착하도록 설계된 코드 버전을 사용하는 것이 유용 할 수 있습니다. 누수 될 수있는 내용은 0이되거나 기밀이 아닌 데이터로 덮어 씁니다.
내가 알 수 있듯이 C ++ 표준은 이러한 행동 중 하나가 명령을 정당화하는 데 다른 것보다 충분히 유용하다고 말하지 않습니다. 아이러니하게도, 이러한 사양 부족은 최적화를 용이하게하기위한 것이지만, 프로그래머가 어떤 종류의 약한 행동 보장을 이용할 수 없다면 최적화가 무효화됩니다.
의 모든 멤버는 Data
기본 유형 이므로의 모든 멤버의 data2
정확한 "비트 단위 복사"를 data
받습니다. 따라서의 값은의 값과 data2.b
정확히 같습니다 data.b
. 그러나 data.b
명시 적으로 초기화하지 않았으므로 정확한 값을 예측할 수 없습니다. 에 할당 된 메모리 영역의 바이트 값에 따라 다릅니다 data
.
std::memcpy
std::memcpy
std::memmove