C에서 겹치는 객체의 의미는 무엇입니까?


25

다음 구조를 고려하십시오.

struct s {
  int a, b;
};

일반적으로 1 이면이 구조체의 크기는 8이고 정렬은 4입니다.

두 개의 struct s객체를 생성하고 (보다 정확하게는 두 개의 객체를 할당 된 저장소에 쓰면) 두 번째 객체가 첫 번째 객체와 겹치는 경우 어떻게해야합니까?

char *storage = malloc(3 * sizeof(struct s));
struct s *o1 = (struct s *)storage; // offset 0
struct s *o2 = (struct s *)(storage + alignof(struct s)); // offset 4

// now, o2 points half way into o1
*o1 = (struct s){1, 2};
*o2 = (struct s){3, 4};

printf("o2.a=%d\n", o2->a);
printf("o2.b=%d\n", o2->b);
printf("o1.a=%d\n", o1->a);
printf("o1.b=%d\n", o1->b);

이 프로그램에 대해 정의되지 않은 동작이 있습니까? 그렇다면 어디에 정의되지 않습니까? UB가 아닌 경우 항상 다음을 인쇄해야합니다.

o2.a=3
o2.b=4
o1.a=1
o1.b=3

특히, 겹치는 객체 가 쓰여질 o1때 가리키는 객체에 어떤 일이 발생하는지 알고 싶습니다 o2. 여전히 클로저되지 않은 부분 ( o1->a) 에 액세스 할 수 있습니까? clobbered 부분에 액세스하는 것은 o1->b단순히 액세스하는 것과 동일 o2->a합니까?

어떻게 효과적인 유형은 여기에 적용? 겹치지 않는 객체와 마지막 저장소와 동일한 위치를 가리키는 포인터에 대해 이야기 할 때는 규칙이 명확하지만, 객체의 유효 유형 또는 겹치는 객체에 대해 이야기 할 때는 덜 명확합니다.

두 번째 쓰기가 다른 유형 인 경우 어떤 것이 변경됩니까? 회원이 있다면 말 intshort아닌 두 가지 int의?

여기에서 놀고 싶다면 godbolt 가 있습니다.


1 이 답변은 그렇지 않은 플랫폼에 적용됩니다. 예를 들어 일부는 크기가 4이고 정렬이 2 일 수 있습니다. 크기와 정렬이 동일한 플랫폼에서는 정렬 된 겹치는 객체 때문에이 질문이 적용되지 않습니다. 불가능하지만 그런 플랫폼이 있는지 확실하지 않습니다.


2
나는 그것이 UB라고 확신하지만, 언어 변호사가 장과 구절을 제공하게 할 것입니다.
Barmar

구식 Cray 벡터 시스템의 C 컴파일러는 ILP64 모델과 강제 64 비트 정렬 (주소는 64 비트 워드-바이트 주소 지정 없음)을 사용하여 정렬 및 크기를 동일하게 강제한다고 생각합니다. 물론 이것은 많은 다른 문제들을 일으켰습니다 ....
John D McCalpin

답변:


15

기본적으로 이것은 표준의 모든 회색 영역입니다. 엄격한 앨리어싱 규칙은 기본 사례를 지정하고 세부 정보를 작성하도록 독자 (및 컴파일러 공급 업체)를 남겨 둡니다.

더 나은 규칙을 작성하려는 노력이 있었지만 지금까지는 규범적인 텍스트를 얻지 못했으며 C2x의 상태가 확실하지 않습니다.

이전 질문에 대한 나의 답변에서 언급했듯이, 가장 일반적인 해석은 우리가 적용을 계속하더라도 p->q수단 (*p).q유효 유형 이 모두에 적용된다는 것입니다 . *p.q

이 해석에서는 위치 printf("o1.a=%d\n", o1->a);유효 유형 이 (위치의 일부를 덮어 썼기 때문에) 정의되지 않았 *o1으므로 정의되지 않은 동작이 발생 s합니다.

이 해석의 이론적 근거는 다음과 같은 기능에서 볼 수 있습니다.

void f(s* s1, s* s2)
{
    s2->a = 5;
    s1->b = 6;
    printf("%d\n", s2->a);
}

이 해석을 통해 마지막 행은에 최적화 될 수 puts("5");있지만,이를 사용하지 않으면 컴파일러는 함수 호출이 f(o1, o2);엄격한 별칭 지정 규칙에 의해 제공되는 모든 이점을 잃었을 수도 있다는 점을 고려해야합니다 .

비슷한 논거 int가 서로 다른 오프셋에 멤버 를 갖는 두 개의 관련되지 않은 구조체 유형에 적용됩니다 .


1
f(s* s1, s* s2)하지 않고, restrict컴파일러는 가정 할 수 s1s2다른 포인터입니다. 나는 생각 을 다시하지 않고, restrict심지어 그들이 부분적으로 중복되지 않는 가정 할 수 없다. IAC, 나는 OP의 관심사가 f()유추에 의해 잘 시연되는 것을 보지 못했다 . 잘자요. 전반기 UV.
chux-

제한없이 @ chux-ReinstateMonica s1 == s2는 허용되지만 부분 겹침은 허용되지 않습니다. (내 코드 예제에서 최적화를 수행 할 수있는 경우 s1 == s2)
MM

@ chux-ReinstateMonica 또한 int구조체 대신 (와 시스템 _Alignof(int) < sizeof(int)) 대신 동일한 문제를 고려할 수도 있습니다 .
MM

3
C2x의 유효 유형에 관한 이런 종류의 질문의 상태는 거의 공개되어 있으며 연구 그룹에서 여전히 논쟁의 대상입니다. 의 동등성을 주장 조심하지만 수 p->q(*p).q. 이것은 사용자가 말한 형식 해석에 해당 될 수 있지만 운영 관점에서는 그렇지 않습니다. 멤버의 액세스가 다른 멤버의 액세스를 의미하지 않는 동일한 구조에 대한 동시 액세스가 중요합니다.
Jens Gustedt

엄격한 앨리어싱 규칙은 액세스 에 관한 것 입니다. 에서 왼쪽 표현 E1.E2표현은 내가 전체 평균 (액세스를 수행하지 않는 E1경우. 그 하위 표현식의 일부가 액세스를 수행 할 수있다. 표현, 즉 E1(*p)평가 때 다음 포인터 값을 읽기 p액세스하지만, 평가 *p또는 (*p)어떤을 수행하지 않습니다 접속하다). 액세스가없는 경우 엄격한 앨리어싱 규칙이 적용되지 않습니다.
언어 변호사
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.