あなたの例で*(p1 + 1) = 10;
は、サイズ1の配列の終わりを過ぎているため、UBである必要があります。ただし、配列がより大きなchar配列で動的に構築されているため、ここでは非常に特殊なケースになります。
動的オブジェクトの作成については、4.5 C ++オブジェクトモデル[intro.object]、C ++標準のn4659ドラフトの§3で説明されています。
3タイプ「arrayofNunsignedchar」またはタイプ「arrayofN 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)—そのタイプのnullポインター値(7.11)、または
(3.4)—無効なポインター値。
オブジェクトの終わりへの、またはオブジェクトの終わりを超えたポインターであるポインター型の値は、オブジェクトによって占有されているメモリー(4.4)の最初のバイト、またはオブジェクトによって占有されているストレージの終了後のメモリーの最初のバイトのアドレスを表し
ます。 、それぞれ。[注:オブジェクト(8.7)の終わりを超えたポインターは、無関係なものを指しているとは見なされません。そのアドレスにある可能性のあるオブジェクトのタイプのオブジェクト。ポインタ値は、それが示すストレージがそのストレージ期間の終わりに達すると無効になります。6.7を参照してください。—end note]ポインタ演算(8.7)および比較(8.9、8.10)の目的で、n個の要素の配列xの最後の要素の終わりを超えたポインタは、仮想要素x [へのポインタと同等であると見なされます。 n]。ポインタ型の値表現は実装によって定義されます。レイアウト互換タイプへのポインタは、同じ値表現と配置要件(6.11)を持っている必要があります。
ノートエンド過去のAポインタ...オブジェクトはが指すので、ここでは適用されないp1
とp2
していない無関係な、しかし、ポインタ算術演算は、ストレージを提供するオブジェクトの内部で意味をなすので、同じ完全なオブジェクトにネストされています: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
次に、配列の最初の要素へのポインタとして使用できます。