Programming

비활성 조합원 및 정의되지 않은 동작에 액세스하고 있습니까?

procodes 2020. 7. 27. 08:08
반응형

비활성 조합원 및 정의되지 않은 동작에 액세스하고 있습니까?


union마지막 세트 이외의 멤버에 액세스하는 것이 UB 라는 인상을 받았지만 견실 한 참조를 찾을 수없는 것 같습니다 (UB를 주장하는 답변 외에 표준의 지원이없는 답변).

정의되지 않은 동작입니까?


C ++ ( )에는 그러한 권한이없는 반면 C는 명시 적으로 공용체를 통해 유형 punning을 허용합니다.

6.5.2.3 구조와 노조원

95) 공용체 객체의 내용을 읽는 데 사용 된 멤버가 객체에 값을 저장하는 데 사용한 마지막 멤버와 동일하지 않은 경우 값의 객체 표현의 해당 부분이 새 객체의 객체 표현으로 해석됩니다. 6.2.6에 기술 된 유형 (때때로``유형 punning ''이라고하는 프로세스). 이것은 트랩 표현 일 수 있습니다.

C ++의 상황 :

9.5 조합 [class.union]

공용체에서, 비 정적 데이터 멤버 중 최대 하나는 언제든지 활성화 될 수 있습니다. 즉, 비 정적 데이터 멤버 중 최대 하나의 값은 언제든지 통합에 저장 될 수 있습니다.

C ++은 나중에 struct공통 초기 시퀀스를 가진을 포함하는 공용체를 사용할 수있는 언어를 가지고 있습니다 . 그러나 이것은 유형 제거를 허용하지 않습니다.

C ++에서 공용체 유형 제거 허용 되는지 확인하려면 추가로 검색해야합니다. 리콜 것을 위한 표준적인 기준은 C ++ 11 (C11 및 C99은 유니온 타입 - 말장난 허용 유사한 언어가) :

3.9 유형 [기본 유형]

4-T 유형의 오브젝트의 오브젝트 표시는 T 유형의 오브젝트가 차지하는 N 개의 부호없는 char 오브젝트의 시퀀스입니다. 여기서 N은 sizeof (T)와 같습니다. 객체의 값 표현은 유형 T의 값을 보유하는 비트 세트입니다. 사소하게 복사 가능한 유형의 경우, 값 표현은 값을 결정하는 객체 표현의 비트 세트입니다. 이는 구현의 개별 요소입니다. 정의 된 값 집합. 42
42) C ++의 메모리 모델은 ISO / IEC 9899 프로그래밍 언어 C의 메모리 모델과 호환됩니다.

읽을 때 특히 흥미 롭습니다

3.8 객체 수명 [basic.life]

유형 T의 객체 수명은 다음과 같은 경우에 시작됩니다. — 유형 T에 대한 적절한 정렬 및 크기를 가진 스토리지가 확보되고 — 객체가 사소한 초기화가있는 경우 객체의 초기화가 완료됩니다.

따라서 공용체에 포함 된 기본 유형 ( ipso 사실상 간단한 초기화가 있음)의 경우 객체의 수명은 최소한 연합 자체의 수명을 포함합니다. 이것은 우리가 호출 할 수 있습니다

3.9.2 화합물 유형 [기본 화합물]

T 유형의 개체가 주소 A에있는 경우 값이 주소 A 인 cv T * 유형의 포인터는 값을 얻는 방법에 관계없이 해당 개체를 가리키는 것으로 간주됩니다.

우리가 관심있는 작업이 type-punning이라고 가정합니다. 즉, 비 활동 노조원의 가치를 취하는 것으로 가정하면, 그에 따라 해당 회원이 참조하는 객체에 대한 유효한 참조가 있다고 위에서 말하면, 그 작업은 lvalue-to -r 값 변환 :

4.1 L 값에서 R 값으로의 변환 [전환]

비 기능 비 배열 유형 T의 glvalue는 prvalue로 변환 될 수 있습니다. 경우 T불완전한 유형, 잘못 형성되는이 변환을 필요로하는 프로그램입니다. glvalue가 참조하는 객체가 유형의 객체 T가 아니며에서 파생 된 유형의 객체가 아니 T거나 객체가 초기화되지 않은 경우이 변환이 필요한 프로그램은 정의되지 않은 동작을합니다.

문제는 비 활동 공용체 구성원 인 오브젝트가 활성 공용체 구성원에 저장하여 초기화되는지 여부입니다. 내가 알 수있는 한, 이것은 사실이 아니므로 다음과 같은 경우에도 마찬가지입니다.

  • 공용체가 char배열 스토리지 로 복사되어 다시 복사 되거나 (3.9 : 2)
  • 공용체가 동일한 유형 (3.9 : 3)의 다른 공용체에 바이트 단위로 복사되거나
  • ISO / IEC 9899 (정의 된 한) (3.9 : 4 주 42)를 따르는 프로그램 요소에 의해 언어 경계를 넘어서 연합에 접근 한 후

비 활동 구성원에 의한 통합에 대한 액세스 가 정의 되고 오브젝트 및 값 표시를 따르도록 정의되며, 위의 개입 중 하나가없는 액세스는 정의되지 않은 동작입니다. 구현은 물론 정의되지 않은 동작이 발생하지 않는다고 가정 할 수 있기 때문에 이는 그러한 프로그램에서 수행 될 수있는 최적화에 영향을 미칩니다.

즉, 우리는 비 활동 조합원에게 합법적으로 lvalue를 구성 할 수 있지만 (건축하지 않고 비 활동 회원에게 할당하는 것이 괜찮은 이유) 초기화되지 않은 것으로 간주됩니다.


C ++ 11 표준은 이렇게 말합니다

9.5 조합

공용체에서, 비 정적 데이터 멤버 중 최대 하나는 언제든지 활성화 될 수 있습니다. 즉, 비 정적 데이터 멤버 중 최대 하나의 값은 언제든지 통합에 저장 될 수 있습니다.

하나의 값만 저장된 경우 다른 값을 어떻게 읽을 수 있습니까? 그냥 거기에 없습니다.


gcc 문서는 이것을 구현 정의 동작 아래에 나열합니다.

  • 다른 유형의 멤버 (C90 6.3.2.3)를 사용하여 통합 개체의 멤버에 액세스합니다.

객체 표현의 관련 바이트는 액세스에 사용되는 유형의 객체로 취급됩니다. 유형 제거를 참조하십시오. 이것은 트랩 표현 일 수 있습니다.

이는 C 표준에 필요하지 않음을 나타냅니다.


2016-01-05 : 의견을 통해 C 표준 문서에 각주와 유사한 텍스트를 추가하는 C99 결함 보고서 # 283 에 연결되었습니다 .

78a) 공용체 객체의 내용에 액세스하는 데 사용 된 멤버가 객체에 값을 저장하는 데 사용한 마지막 멤버와 동일하지 않은 경우 값의 객체 표현의 해당 부분이 새 객체의 객체 표현으로 해석됩니다. 6.2.6에 기술 된 유형 (종종 "유형 punning"이라고하는 프로세스). 이것은 트랩 표현 일 수 있습니다.

각주가 표준에 대한 규범이 아니라는 점을 고려하면 많은 설명이 있는지 확실하지 않습니다.


나는 표준이 정의되지 않은 행동이라고 말하는 가장 가까운 것은 공통 초기 시퀀스 (C99, §6.5.2.3 / 5)를 포함하는 노동 조합의 행동을 정의하는 위치라고 말합니다.

One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the complete type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

C++11 gives similar requirements/permission at §9.2/19:

If a standard-layout union contains two or more standard-layout structs that share a common initial sequence, and if the standard-layout union object currently contains one of these standard-layout structs, it is permitted to inspect the common initial part of any of them. Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types and either neither member is a bit-field or both are bit-fields with the same width for a sequence of one or more initial members.

Though neither states it directly, these both carry a strong implication that "inspecting" (reading) a member is "permitted" only if 1) it is (part of) the member most recently written, or 2) is part of a common initial sequence.

That's not a direct statement that doing otherwise is undefined behavior, but it's the closest of which I'm aware.


Something that is not yet mentioned by available answers is the footnote 37 in the paragraph 21 of the section 6.2.5:

Note that aggregate type does not include union type because an object with union type can only contain one member at a time.

This requirement seem to clearly imply that you must not write in a member and read in another one. In this case it might be undefined behavior by lack of specification.


I well explain this with a example.
assume we have the following union:

union A{
   int x;
   short y[2];
};

I well assume that sizeof(int) gives 4, and that sizeof(short) gives 2.
when you write union A a = {10} that well create a new var of type A in put in it the value 10.

your memory should look like that: (remember that all of the union members get the same location)

       |                   x                   |
       |        y[0]       |       y[1]        |
       -----------------------------------------
   a-> |0000 0000|0000 0000|0000 0000|0000 1010|
       -----------------------------------------

as you could see, the value of a.x is 10, the value of a.y1 is 10, and the value of a.y[0] is 0.

now, what well happen if I do this?

a.y[0] = 37;

our memory will look like this:

       |                   x                   |
       |        y[0]       |       y[1]        |
       -----------------------------------------
   a-> |0000 0000|0010 0101|0000 0000|0000 1010|
       -----------------------------------------

this will turn the value of a.x to 2424842 (in decimal).

now, if your union has a float, or double, your memory map well be more of a mess, because of the way you store exact numbers. more info you could get in here.

참고 URL : https://stackoverflow.com/questions/11373203/accessing-inactive-union-member-and-undefined-behavior

반응형