C ++の新しいint [0] —メモリを割り当てますか?


240

簡単なテストアプリ:

cout << new int[0] << endl;

出力:

0x876c0b8

したがって、動作するように見えます。これについて規格は何と言っていますか?空のメモリブロックを「割り当てる」ことは常に合法ですか?


4
+1非常に興味深い質問-実際のコードでどれほど重要かはわかりませんが。
Zifre 2009

37
@Zifre:私は好奇心を求めていますが、実際に問題になる可能性があります。たとえば、割り当てられたメモリブロックのサイズが何らかの方法で計算され、計算結果がゼロになる場合は、例外を直接追加する必要はありません。サイズがゼロのブロックを割り当てないようにします。サイズがゼロのブロックだけが逆参照されない場合は、エラーなく割り当ておよび削除する必要があるためです。したがって、一般的に、これはメモリブロックとは何かをより広く抽象化します。

2
@ emg-2:例の状況では、delete []はNULLポインターに対して完全に合法であるため、実際には問題になりません:-)。
エヴァン・テラン

2
それは接線的に関連しているだけなので、ここでコメントしますが、C ++は多くの点で、明示的にストレージを必要としない場合でも、異なるオブジェクトが一意のアドレスを持つことを保証します。関連する実験は、空の構造体のサイズをチェックすることです。または、その構造体の配列。
Drew Dormann、

2
Shmooptyのコメントを詳しく説明すると、特にテンプレート(例:std :: allocatorのようなポリシークラステンプレート)を使用してプログラミングする場合、C ++ではサイズがゼロのオブジェクトを持つのが一般的です。一般的なコードでは、そのようなオブジェクトを動的に割り当て、オブジェクトへのポインタを使用してオブジェクトのアイデンティティを比較する必要がある場合があります。これが、演算子new()がサイズがゼロのリクエストに対して一意のポインタを返す理由です。議論の余地はありませんが、配列の割り当てと演算子new []()にも同じ考え方が当てはまります。
Trevor Robinson

回答:


233

5.3.4 / 7から

direct-new-declaratorの式の値がゼロの場合、要素のない配列を割り当てるために割り当て関数が呼び出されます。

3.7.3.1/2から

サイズがゼロの要求として返されたポインタを逆参照した場合の影響は定義されていません。

また

[new by]によって要求されたスペースのサイズがゼロであっても、要求は失敗する可能性があります。

つまり、それを行うことはできますが、取得したメモリを合法的に(すべてのプラットフォームにわたって明確に定義された方法で)参照解除することはできません。配列の削除にのみ渡すことができます。削除する必要があります。

3.7.3.1/2の文に添付されている興味深い脚注(つまり、標準の規範的部分ではないが、説明の目的で含まれています)です。

[32。その意図は、malloc()またはcalloc()を呼び出すことによってオペレーターnew()を実装可能にすることです。したがって、ルールは実質的に同じです。C ++は、Null以外のポインタを返すためにゼロ要求を要求する点でCと異なります。]


削除しないとメモリリークが発生します。それは期待されていますか?少なくとも私は予想していなかった。
EralpB 2014年

12
@EralpB:反対に、リクエストがゼロの場合でも、この割り当てはヒープで行われます。1つのリクエストは、アロケータによって指定されたゾーンの前後のヒープガードの割り当てと初期化、フリーリストへの挿入、または他の複雑な恐ろしい構造。それを解放することは、後方簿記を行うことを意味します。
v.oddou 2014年

3
@EralpBはい、サイズを問わず、- new[]とバランスをとらないと、メモリリークが発生する可能性がありますdelete[]。特に、呼び出すときnew[i]に、配列のサイズを格納するためにdelete[]
割り当てよ

24

はい、このようなサイズ0の配列を割り当てることは合法です。ただし、それも削除する必要があります。


1
これについての引用はありますか?私たちは皆、int ar[0];違法であることを知っています。なぜ新しいものに問題がないのでしょうか。
モッティ

4
興味深いことに、C ++は、サイズがゼロのオブジェクトを禁止しているため、それほど強力ではありません。空の基本クラスの最適化について考えてみましょう。ここでも、空の基本クラスのサブオブジェクトのサイズがゼロになる場合があります。対照的に、C標準では、サイズがゼロのオブジェクトが作成されないように強力な努力をしています。malloc(0)の定義では、たとえば、ゼロ以外の引数を指定してmallocを実行すると効果があると述べています。そして構造体{...; T n []; }; 配列(FAM)に割り当てられたスペースがない場合は、「n」に1つの要素があるかのように動作することを示します。(どちらの場合でも、xn [0]のように、オブジェクトの使用方法はUBです)
Johannes Schaub-litb

1
sizeof (type)決してゼロを返さないと予想されると思います。例を参照してください:stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet

いわゆる「空の基本クラスの最適化」でさえ関係があるのは、すべてのオブジェクト(オブジェクト内のすべてのバイトではなく)が一意のアドレスを持っている必要があるという主張のためです。ゼロ以外のサイズであることを確認するために、一意のアドレスを持つオブジェクトを実際に処理するコードが必要な場合は、C ++をより単純にすることができます。
スーパーキャット2018年

15

これについて規格は何と言っていますか?空のメモリブロックを「割り当てる」ことは常に合法ですか?

すべてのオブジェクトには一意のID、つまり一意のアドレスがあり、これは長さがゼロ以外であることを意味します(実際のメモリ量は、ゼロバイトを要求した場合、暗黙的に増加します)。

これらのオブジェクトを複数割り当てた場合、それらは異なるアドレスを持っていることがわかります。


「すべてのオブジェクトには一意のID、つまりゼロ以外の長さを意味する一意のアドレスがあります」-正しいが間違っている。オブジェクトにはアドレスがありますが、オブジェクトへのポインタはランダムメモリを指すことができます。「ゼロバイトを要求すると、実際のメモリ量は静かに増加します」-わかりません。operator []また、配列のサイズをどこかに保存します(isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-arrayを参照)。したがって、実装がデータとともにバイトカウントを割り当てる場合、バイトカウントとデータ用に0バイトを割り当てるだけで、最後の1つ前のポインタを返すことができます。
2016

割り当てられたメモリの長さがゼロ以外であり、使用可能なメモリの長さがゼロ以外ではありません。私のポイントは、2つの異なるオブジェクトへの2つのポインタは同じアドレスを指すべきではないということでした。
ChrisW

1
標準(open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)は確かに(3.7.4.1/2)と言っており、への異なる呼び出しoperator new[]は異なるポインターを返すはずです。ところで、にnew expressionはいくつかの追加ルールがあります(5.3.4)。newサイズ0で実際に何かを割り当てる必要があるという手掛かりは見つかりませんでした。申し訳ありませんが、あなたの回答が質問に対する回答ではないことがわかりました。
2016

ブロックの長さを格納するための@Steedメモリは、アドレス空間を使用して暗黙的に計算できます。たとえば、サイズがゼロの配列の場合new[]、動的メモリがマップされていない範囲のアドレスを実装が返すため、実際にはメモリを使用しない(アドレス空間を使用している)
pqnet

@pqnet:良質の実装では、無制限の新規/削除サイクルが許可されるはずですが、許可されるべきではありませんか?64ビットポインターを備えたプラットフォームでは、while(!exitRequested) { char *p = new char[0]; delete [] p; }ポインターをリサイクルせずにループを実行しているコンピューターは、アドレス空間が不足する前に、ゴミになってしまうが、32ビットポインターを備えたプラットフォームでは、はるかに合理的な仮定。
スーパーキャット2018年

14

はい、で0サイズのブロックを割り当てることは完全に合法newです。アクセスできる有効なデータがないため、これを使用して何も実行できません。int[0] = 5;違法です。

しかし、私は基準がmalloc(0)戻るようなものを可能にすることを信じていますNULL

delete []割り当てから取得したポインタも必要です。


5
mallocに関しては、あなたは正しいです-それは実装定義です。これは、多くの場合、誤った機能と見なされます。

興味深い質問だと思います。サイズが0の場合、新しいバージョンのnothrowはNULLを返すことができますか?
エヴァンテラン

1
newとmallocのセマンティクスは、少なくとも標準ではリンクされていません。

彼らがそうであると言っているのではありません... newhのnrowrowバージョンについて特に尋ねていました。
エヴァンテラン

@Evan-newのnothrowバージョンは、リクエストが失敗した場合にのみnullを返します-リクエストサイズが0の場合だけではありません@Neil-規範的にリンクされていません-意図によってリンクされています方法)-私の回答に含まれている脚注を参照してください
Faisal Vali

1

奇妙なことに、C ++では、ゼロバイトが要求された場合でも、オペレーターnewが正当なポインターを返す必要があります。(この奇妙なサウンドの動作が必要なため、言語の他の場所での処理が簡単になります。)

「アイテム51:新規作成および削除の際は慣例に従う」で、Effective C ++ Third Editionがこのように言っているのを発見しました。


0

私がテストしたので、新しいint [0]が余分なスペースを消費することを保証します。

たとえば、次のメモリ使用量

int **arr = new int*[1000000000];

より大幅に小さい

int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
    arr[i]=new int[0];
}

2番目のコードスニペットのメモリ使用量から最初のコードスニペットのメモリ使用量を引いたものが、多数の新しいint [0]に使用されるメモリです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.