귀하의 예에서 *(p1 + 1) = 10;
UB 는 크기 1 의 배열 끝을 지나기 때문에 UB 여야합니다 . 그러나 배열이 더 큰 char 배열에서 동적으로 구성 되었기 때문에 여기서는 매우 특별한 경우입니다.
동적 객체 생성은 C ++ 표준의 n4659 초안 4.5 C ++ 객체 모델 [intro.object] §3에 설명되어 있습니다.
3 "array of N unsigned char"유형 또는 "array of N std :: byte"(21.2.1) 유형의 다른 객체 e와 연관된 스토리지에 완전한 객체가 생성 (8.3.4)되면 해당 어레이는 스토리지를 제공합니다. 생성 된 객체에 대해 다음과 같은 경우 :
(3.1) — e의 수명이 시작되고 끝나지 않았고,
(3.2) — 새 객체의 스토리지가 e 내에 완전히 들어 맞고
(3.3) — 이러한 항목을 충족하는 더 작은 배열 객체가 없습니다. 제약.
3.3은 다소 불분명 해 보이지만 아래 예제는 의도를 더 명확하게합니다.
struct A { unsigned char a[32]; };
struct B { unsigned char b[16]; };
A a;
B *b = new (a.a + 8) B;
int *p = new (b->b + 4) int;
실시 예에 따라서, buffer
어레이는 스토리지 제공 모두 *p1
와 *p2
.
다음 단락 모두에 대한 완전한 객체 것을 증명 *p1
하고 *p2
있습니다 buffer
:
4 다음과 같은 경우 객체 a는 다른 객체 b 내에 중첩됩니다.
(4.1) — a는 b의 하위 객체이거나
(4.2) — b는 a 또는
(4.3)에 대한 스토리지를 제공합니다. — a 가 c 내에 중첩 된 객체 c가있는 경우 , c는 b 내에 중첩됩니다.
5 모든 객체 x에 대해 다음과 같이 결정되는 x의 완전한 객체라고하는 일부 객체가 있습니다.
(5.1) — x가 완전한 객체이면 x의 완전한 객체는 그 자체입니다.
(5.2) — 그렇지 않으면 x의 완전한 객체는 x를 포함하는 (고유 한) 객체의 완전한 객체입니다.
이것이 확립되면 C ++ 17 용 n4659 초안의 다른 관련 부분은 [basic.coumpound] §3 (내 강조)입니다.
3 ... 포인터 유형의 모든 값은 다음 중 하나입니다.
(3.1) — 객체 또는 함수에 대한 포인터 (포인터는 객체 또는 함수를 가리킨다 고 함) 또는
(3.2) — 끝을 지나는 포인터 객체 (8.7) 또는
(3.3)-해당 유형에 대한 널 포인터 값 (7.11) 또는
(3.4)-유효하지 않은 포인터 값.
객체의 끝을 가리키는 포인터이거나 객체의 끝을 지나는 포인터 유형의 값은 객체가 차지하는 메모리 (4.4) 의 첫 번째 바이트 또는 객체가
차지하는 스토리지의 끝 이후 메모리의 첫 번째 바이트의 주소를 나타냅니다. , 각각. [참고 : 개체의 끝 (8.7)을 지나는 포인터는 관련없는 항목 을 가리키는 것으로 간주되지 않습니다.해당 주소에있을 수있는 개체 유형의 개체입니다. 포인터 값은 표시하는 스토리지가 스토리지 기간의 끝에 도달하면 무효화됩니다. 6.7 참조. —end note] 포인터 산술 (8.7) 및 비교 (8.9, 8.10)를 위해 n 개 요소로 구성된 배열 x의 마지막 요소 끝을 지나는 포인터는 가상 요소 x [에 대한 포인터와 동일한 것으로 간주됩니다. 엔]. 포인터 유형의 값 표현은 구현에 따라 정의됩니다. 레이아웃 호환 유형에 대한 포인터는 동일한 값 표현 및 정렬 요구 사항 (6.11)을 가져야합니다.
노트의 끝 과거 포인터는 ... 객체가 가리키는 때문에 여기에 적용되지 않습니다 p1
및 p2
하지 관련이없는 : 포인터를 arithmetics 스토리지를 제공하는 객체 내부 의미하기 때문에,하지만 같은 완전한 개체로 중첩 된 p2 - p1
정의를하고있다 (&buffer[sizeof(int)] - buffer]) / sizeof(int)
그것은 1입니다.
그래서 p1 + 1
에 대한 포인터 *p2
이며 *(p1 + 1) = 10;
동작을 정의하고의 값을 설정했습니다 *p2
.
또한 C ++ 14와 현재 (C ++ 17) 표준 간의 호환성에 대한 C4 부록을 읽었습니다. 단일 문자 배열에서 동적으로 생성 된 객체간에 포인터 산술을 사용할 가능성을 제거하는 것은 IMHO가 일반적으로 사용되는 기능이기 때문에 여기에서 인용해야하는 중요한 변경 사항입니다. 호환성 페이지에 아무것도 없기 때문에 금지하는 것이 표준의 의도가 아님을 확인한 것 같습니다.
특히, 기본 생성자가없는 클래스에서 객체 배열의 일반적인 동적 생성을 무효화합니다.
class T {
...
public T(U initialization) {
...
}
};
...
unsigned char *mem = new unsigned char[N * sizeof(T)];
T * arr = reinterpret_cast<T*>(mem);
for (i=0; i<N; i++) {
U u(...);
new(arr + i) T(u);
}
arr
그런 다음 배열의 첫 번째 요소에 대한 포인터로 사용할 수 있습니다.