- 締切済み
多次元配列の new
多次元配列を new すると、どのような型のサイズの領域の配列が確保されるんでしょうか?たとえば、 int (*a)[2] = new int[3][2]; とすると、 1. 長さ2のintの配列へのポインタ型の長さ3の配列の領域が確保される のか、 2. int[3][2] すなわち、int が 6 つ分の領域が確保される のか。 今まで、「そりゃあ 2 の方だろう」と信じ込んであまり考えずにいたんですが、「コードの型形式からすると 1 の方の解釈でもいいよなぁ」と、ふと思ったものですから、質問させていただきました。 わたしの環境で調べてみると(配列用のハウスキーピング的な余分の領域とか、パディングなどは無視して)、確かに 1 の方なんですか、これで標準準拠なんでしょうかね?^^; XP Home Edition Ver.2002 SP2 cygwin v.1.0.2-1 GNU g++ v.4.1.1 ===== #include <iostream> #include <new> #include <cstdlib> struct A { char a; void *operator new(std::size_t s) { void *p = std::malloc(s); std::cout << "A::new(): " << p << '\t' << s << '\n'; return p; } void operator delete(void *p, std::size_t s) { std::cout << "A::delete(): " << p << '\t' << s << '\n'; if (p) std::free(p); } void *operator new[](std::size_t s) { void *p = std::malloc(s); std::cout << "A::new[](): " << p << '\t' << s << '\n'; return p; } void operator delete[](void *p, std::size_t s) { std::cout << "A::delete[](): " << p << '\t' << s << '\n'; if (p) std::free(p); } }; int main() { std::cout << sizeof(char) << '\t' << sizeof(A) << '\t' << sizeof(A(*)[8]) << '\n'; A *a = new A; std::cout << a << '\n'; A *aa = new A[8]; std::cout << aa << '\n'; A (*aaa)[8] = new A[8][8]; std::cout << aaa << '\n'; A (*aaaa)[8][8] = new A[8][8][8]; std::cout << aaaa << '\n'; delete[] aaaa; delete[] aaa; delete[] aa; delete a; } ===== % ./a.exe 1 1 4 A::new(): 0x870668 1 0x870668 A::new[](): 0x870678 12 0x87067c A::new[](): 0x870688 68 0x87068c A::new[](): 0x8706d0 516 0x8706d4 A::delete[](): 0x8706d0 516 A::delete[](): 0x870688 68 A::delete[](): 0x870678 12 A::delete(): 0x870668 1
- みんなの回答 (2)
- 専門家の回答
みんなの回答
- Werner
- ベストアンサー率53% (395/735)
> ポインタ(int[2]の先頭をさす)が3個取られるというのを表現したかったものですから^^ new int[3][2]; で確保されるのは、 「int型を要素に持つ長さ2の配列」3つ分=int6つ分 の領域で、ポインタのための領域は確保されないです。 (たしか、確保されるのは連続領域のはず) # これで答えになってるかな? 質問者さんが考えた1の解釈ってこんな感じでしょうか? http://upload.wikimedia.org/wikipedia/ja/3/3e/2D_array.png > 多次元配列の new 2 ややこしいのはtypedef使うにこしたことはないですね。 一生懸命typedefなしで考えても、後々そのコード読めなくなりそうだし(笑)
- Werner
- ベストアンサー率53% (395/735)
質問の答えはよく分かりませんが、 読んでいてちょっと気になったところがあったので。 > 1. 長さ2のintの配列へのポインタ型の長さ3の配列の領域が確保される これは、 「int型を要素に持つ長さ2の配列」を要素に持つ長さ3の配列 の領域 ではないでしょうか。 冒頭のコードを分けて書くとこんな感じになりますし。 typedef int T[2]; int (*a)[2] = new T[3]; サイズだけならどちらの解釈でも良さそうな気もしますが、 型を考慮するなら1の解釈の方が正確かな?とは思います。 # ついでに、私の環境での結果 Microsoft Visual Studio 2005 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86 ==== 1 1 4 A::new(): 00372E30 1 00372E30 A::new[](): 00372E40 8 00372E40 A::new[](): 00372E50 64 00372E50 A::new[](): 00372E98 512 00372E98 A::delete[](): 00372E98 1 A::delete[](): 00372E50 1 A::delete[](): 00372E40 1 A::delete(): 00372E30 1
お礼
回答ありがとうございます。 すみません。わたしの日本語がおかしかったですかね^^; 「「int型を要素に持つ長さ2の配列」を要素に持つ長さ3の配列 の領域」だと、結局、連続しているかどうかは別として、int の領域が 6 個取られることになるので、そうじゃなくて、ポインタ(int[2]の先頭をさす)が3個取られるというのを表現したかったものですから^^ ポインタも int も 4 バイトとして、1 の解釈なら12バイト、2 の解釈なら 24 バイトの領域を確保するだろうという意味です^^; Werner さんが試してくださった結果をみても、やはり 2 の解釈が標準の規約に沿っているのでしょうね。
補足
同じ Werner さんなので、<多次元配列の new 2>のほうの補足ですが^^; わたしが型を書き間違えていたというのは、以下の意味です。 typeinfo で書き出してみると char ((*p)[8])[5]; char (*q[8])[5]; std::cout << typeid(char((*)[8])[5]).name() << '\n'; std::cout << typeid(char(*[8])[5]).name() << '\n'; std::cout << typeid(p).name() << '\n'; std::cout << typeid(q).name() << '\n'; ↓ PA8_A5_c A8_PA5_c PA8_A5_c A8_PA5_c
お礼
そうですそうです。代入先の型だと、ドープベクタみたいでもいいのかな、と思いまして^^ で、配列の先頭を指すポインタの配列の領域だけが確保される、みたいな。。 ちなみに、Werner さんの環境では、クラス専用の delete[] オペレータのサイズ引数のサポートはなされていないみたいですね(みんな、1 になってる^^)。 ありがとうございます。環境によって、サイズ引数のサポートがなされないこともある、ということも学べましたし^^
補足
Java なんかだと、ドープベクタのようなので、 int a[][] = new int[n][]; for (int i = 0; i < n; ++i) a[i] = new int[n]; なんてことができますけどね^^