programing

한 기능에서 선언된 연합 유형을 다른 기능에서 사용하는 것이 잘못된 이유는 무엇입니까?

lastmoon 2023. 8. 10. 19:08
반응형

한 기능에서 선언된 연합 유형을 다른 기능에서 사용하는 것이 잘못된 이유는 무엇입니까?

ISO/IEC 9899:1999(6.5.2.3 참조)를 읽었을 때 다음과 같은 예를 보았습니다(광산 강조).

다음은 유효한 조각이 아닙니다(함수 에서 유니언 유형이 표시되지 않기 때문입니다).

struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 * p1, struct t2 * p2)
{
      if (p1->m < 0)
            p2->m = -p2->m;
      return p1->m;
}
int g()
{
      union {
            struct t1 s1;
            struct t2 s2;
      } u;
      /* ... */
      return f(&u.s1, &u.s2);
}

테스트를 해보니 오류와 경고가 없었습니다.

제 질문은:이 조각이 잘못된 이유는 무엇입니까?

이 예제는 사전에1 단락을 설명하려고 시도합니다(내 항목 강조).

6.5.2.3 6

조합의 사용을 단순화하기 위해 한 가지 특별한 보증이 제공됩니다. 조합이 공통의 초기 순서를 공유하는 여러 구조를 포함하는 경우(아래 참조), 그리고 조합 객체가 현재 이러한 구조 중 하나를 포함하는 경우,완성된 조합 유형의 선언문이 보이는 모든 곳에서 그들의 공통 초기 부분을 검사하는 것이 허용됩니다.해당 멤버가 하나 이상의 초기 멤버 시퀀스에 대해 호환되는 유형(및 비트 필드의 경우 동일한 너비)을 가질 경우 두 구조는 공통의 초기 시퀀스를 공유합니다.

때부터f는 에선언됨 앞에 됩니다.g게다가 이름 없는 조합 유형은 에 로컬입니다.g조합 유형이 보이지 않는다는 것은 의심의 여지가 없습니다.f.

이 예제에서는 방법을 보여주지 않습니다.u초기화되어 , 으로 쓴 글이 초화되었구성마라고 합니다.u.s2.m는 함가검사때정않의동있은습을 되지 않은 .p1->m공통의 초기 시퀀스 보증이 효력을 발휘하지 않는 경우.

반대의 경우에도 마찬가지입니다.u.s1.m호출 은 기능호전마기로것록으로된것액하, 다보는스세으출입니다.p2->m정의되지 않은 동작입니다.

:f그 자체는 유효하지 않습니다.이것은 완벽하게 합리적인 기능 정의입니다.정의되지 않은 동작은 해당 동작으로 전달됩니다.&u.s1그리고.&u.s2이 바로 않은 행동을 것입니다.그것이 정의되지 않은 행동을 야기하는 것입니다.


1 C11 표준 초안인 n1570을 인용합니다.그러나 사양은 동일해야 하며 한 단락 또는 두 단락을 위/아래로 이동해야 합니다.

다음은 엄밀한 앨리어싱 규칙입니다. C(또는 C++) 컴파일러에 의해 만들어진 한 가지 가정은 다른 유형의 객체에 대한 참조 해제 포인터가 결코 동일한 메모리 위치를 참조하지 않을 것이라는 것입니다(즉, 서로 별칭 지정).

이 함수

int f(struct t1* p1, struct t2* p2);

는 것으로 합니다.p1 != p2공식적으로 다른 유형을 가리키기 때문입니다.결과적으로 최적화자는 다음과 같이 가정할 수 있습니다.p2->m = -p2->m; 영향을 미치지 .p1->m 먼저 값읽 수있 다니습의 수 .p1->m에는 0보다 작은 에는 0을 수행합니다.p2->m = -p2->m;마지막으로 레지스터 값을 변경하지 않고 반환합니다!

이 여기연유방일다법니입한합이다를 만드는 입니다.p1 == p2모든 조합원이 동일한 주소를 가지고 있기 때문에 이진 수준에서.

다른 예:

struct t1 { int m; };
struct t2 { int m; };

int f(struct t1* p1, struct t2* p2)
{
    if (p1->m < 0) p2->m = -p2->m;
    return p1->m;
}

int g()
{
    union {
        struct t1 s1;
        struct t2 s2;
    } u;
    u.s1.m = -1;
    return f(&u.s1, &u.s2);
}

해야 할 일g 답례?+1로 바꿉니다.)f. . .gcc의 gcc 생성어셈블리입니다.-O1 기능

f:
        cmp     DWORD PTR [rdi], 0
        js      .L3
.L2:
        mov     eax, DWORD PTR [rdi]
        ret
.L3:
        neg     DWORD PTR [rsi]
        jmp     .L2
g:
        mov     eax, 1
        ret

지금까지는 예외입니다.하지만 우리가 그것을 시도할 때.-O2

f:
        mov     eax, DWORD PTR [rdi]
        test    eax, eax
        js      .L4
        ret
.L4:
        neg     DWORD PTR [rsi]
        ret
g:
        mov     eax, -1
        ret

반환 값이 하드 코딩되었습니다.-1

는 는이유 때문입니다.f는 처에는 .p1->m에 시대에eaxregister )mov eax, DWORD PTR [rdi]) 에 다시 읽지 않습니다.p2->m = -p2->m;(neg DWORD PTR [rsi] - 됩니다 ) - 반됩니다환.eax


여기서 사용되는 union은 union 객체의 정적이 아닌 모든 데이터 멤버에 대해 동일한 주소를 가집니다.&u.s1 == &u.s2.

어셈블리어 코드를 이해하지 못하는 사용자이며, c/c++에서 앨리어싱이 f 코드에 미치는 영향을 보여줄 수 있습니다.

int f(struct t1* p1, struct t2* p2)
{
    int a = p1->m;
    if (a < 0) p2->m = -p2->m;
    return a; 
}

캐시 파일입니다.p1->m var 은 다음과 .a(물론 등록부에 기재되어 있음) 그리고 그것을 반환합니다, 그럼에도 불구하고.p2->m = -p2->m;p1->m하지만 컴파일러는 다음과 같이 가정합니다.p1메모리는 영향을 받지 않습니다, 왜냐하면 그것은 그것이 가정하기 때문입니다.p2중복되지 않는 다른 메모리를 가리킵니다.p1

따라서 다른 컴파일러와 다른 최적화 수준에서 동일한 소스 코드가 다른 값(-1 또는 +1)을 반환할 수 있습니다. 따라서 정의되지 않은 동작은 그대로입니다.

공통 초기 시퀀스 규칙의 주요 목적 중 하나는 함수가 많은 유사한 구조에서 상호 교환적으로 작동할 수 있도록 하는 것입니다.컴파일러가 구조에 작용하는 함수가 공통의 초기 시퀀스를 공유하는 다른 구조에서 해당 멤버를 변경할 수 있다고 가정하도록 요구하면 유용한 최적화가 손상됩니다.

공통 초기 시퀀스 보증에 의존하는 대부분의 코드는 쉽게 인식할 수 있는 몇 가지 패턴을 사용합니다.

struct genericFoo {int size; short mode; };
struct fancyFoo {int size; short mode, biz, boz, baz; };
struct bigFoo {int size; short mode; char payload[5000]; };

union anyKindOfFoo {struct genericFoo genericFoo;
  struct fancyFoo fancyFoo;
  struct bigFoo bigFoo;};

...
if (readSharedMemberOfGenericFoo( myUnion->genericFoo ))
  accessThingAsFancyFoo( myUnion->fancyFoo );
return readSharedMemberOfGenericFoo( myUnion->genericFoo );

서로 하는 기능에 하면서, 이 한다고 이 표준의 저자들은 호출된 기능 내에서 조합 유형의 가시성이 기능이 예를 들어 필드에 대한 접근 가능성을 인식해야 하는지 여부를 결정하는 요소가 되어야 한다고 명시했습니다.mode상당한FancyFoo현장에 영향을 미칠 수 있음mode상당한genericFoo가 전달될 수 하는 유니언을 가 전 달 가 사 요 하 구 는 야 져 결 합 는 될readSharedMemberOfGeneric이 함수와 동일한 컴파일 단위에서 공통 초기 시퀀스 규칙은 그렇지 않은 경우보다 덜 유용하지만 적어도 위와 같은 패턴을 사용할 수 있게 합니다.

조합 될 수 것으로 이지 않은 가 될 했고, 이 방법을 하지 않기 에 gcc clang과 clang은 다음과 같이 했습니다.조물에 관련된 유형이 포함될 수 있음을 나타내는 것으로 간주하는 것은 실용적이지 않은 최적화 방해가 될 것이라고 생각하였고, 이 기준서는 그들이 다른 방법을 통해 그러한 구조물을 지원하도록 요구하지 않기 때문에,그들은 전혀 그들을 지지하지 않을 것입니다.따라서 공통 초기 시퀀스 보장을 의미 있는 방식으로 활용해야 하는 코드의 실제 요구 사항은 유니온 유형 선언을 볼 수 있도록 하는 것이 아니라 clang과 gcc를 사용하여 호출하는 것입니다.-fno-strict-aliasing플래그. 또한 실용적일 때 가시적인 노조 선언을 포함하는 것은 해롭지 않겠지만, gcc와 cang으로부터 올바른 행동을 보장하기에는 필요하지도 충분하지도 않습니다.

언급URL : https://stackoverflow.com/questions/52511896/why-is-it-invalid-for-a-union-type-declared-in-one-function-to-be-used-in-anothe

반응형