다음 구조를 고려하십시오.
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
합니까?
어떻게 효과적인 유형은 여기에 적용? 겹치지 않는 객체와 마지막 저장소와 동일한 위치를 가리키는 포인터에 대해 이야기 할 때는 규칙이 명확하지만, 객체의 유효 유형 또는 겹치는 객체에 대해 이야기 할 때는 덜 명확합니다.
두 번째 쓰기가 다른 유형 인 경우 어떤 것이 변경됩니까? 회원이 있다면 말 int
과 short
아닌 두 가지 int
의?
여기에서 놀고 싶다면 godbolt 가 있습니다.
1 이 답변은 그렇지 않은 플랫폼에 적용됩니다. 예를 들어 일부는 크기가 4이고 정렬이 2 일 수 있습니다. 크기와 정렬이 동일한 플랫폼에서는 정렬 된 겹치는 객체 때문에이 질문이 적용되지 않습니다. 불가능하지만 그런 플랫폼이 있는지 확실하지 않습니다.
답변
기본적으로 이것은 표준의 모든 회색 영역입니다. 엄격한 앨리어싱 규칙은 기본 사례를 지정하고 세부 정보를 작성하도록 독자 (및 컴파일러 공급 업체)를 남겨 둡니다.
더 나은 규칙을 작성하려는 노력이 있었지만 지금까지는 규범적인 텍스트를 얻지 못했으며 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
가 서로 다른 오프셋에 멤버 를 갖는 두 개의 관련되지 않은 구조체 유형에 적용됩니다 .