• 締切済み

多次元配列の 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

みんなの回答

  • Werner
  • ベストアンサー率53% (395/735)
回答No.2

> ポインタ(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なしで考えても、後々そのコード読めなくなりそうだし(笑)

mikaemi
質問者

お礼

そうですそうです。代入先の型だと、ドープベクタみたいでもいいのかな、と思いまして^^ で、配列の先頭を指すポインタの配列の領域だけが確保される、みたいな。。 ちなみに、Werner さんの環境では、クラス専用の delete[] オペレータのサイズ引数のサポートはなされていないみたいですね(みんな、1 になってる^^)。 ありがとうございます。環境によって、サイズ引数のサポートがなされないこともある、ということも学べましたし^^

mikaemi
質問者

補足

Java なんかだと、ドープベクタのようなので、  int a[][] = new int[n][];  for (int i = 0; i < n; ++i) a[i] = new int[n]; なんてことができますけどね^^

  • Werner
  • ベストアンサー率53% (395/735)
回答No.1

質問の答えはよく分かりませんが、 読んでいてちょっと気になったところがあったので。 >  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

mikaemi
質問者

お礼

回答ありがとうございます。 すみません。わたしの日本語がおかしかったですかね^^; 「「int型を要素に持つ長さ2の配列」を要素に持つ長さ3の配列 の領域」だと、結局、連続しているかどうかは別として、int の領域が 6 個取られることになるので、そうじゃなくて、ポインタ(int[2]の先頭をさす)が3個取られるというのを表現したかったものですから^^ ポインタも int も 4 バイトとして、1 の解釈なら12バイト、2 の解釈なら 24 バイトの領域を確保するだろうという意味です^^; Werner さんが試してくださった結果をみても、やはり 2 の解釈が標準の規約に沿っているのでしょうね。

mikaemi
質問者

補足

同じ 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

関連するQ&A

  • 多次元配列の new 2

    追加の質問ですみません^^; char の8個の配列へのポインタの配列を new する場合などは、以下のサンプルのように typedef しないとかけないんでしょうかね?たとえば、  char (**bb)[8] = new (char (*)[8])[8]; 書きたいように思いますが、これは文法違反ですし・・・^^; ==== サンプル:(iostream の初期化時に、定義した new が呼び出されるかもしれないことを一応考慮して、stdio の関数を使っています^^) #include <new> #include <stdlib.h> #include <stdio.h> void *operator new(std::size_t s) { void *p = malloc(s); fprintf(stderr, "::new(): %p\t%lu\n", p, (unsigned long)s); return p; } void operator delete(void *p) { fprintf(stderr, "::delete(): %p\n", p); if (p) free(p); } void *operator new[](std::size_t s) { void *p = malloc(s); fprintf(stderr, "::new[](): %p\t%lu\n", p, (unsigned long)s); return p; } void operator delete[](void *p) { fprintf(stderr, "::delete[](): %p\n", p); if (p) free(p); } int main() { typedef char (*T)[8]; char (**b)[8] = new T[8]; delete[] b; } ==== % ./a.exe ::new[](): 0x6e01b0 32 ::delete[](): 0x6e01b0

  • クラス内にnewで形成した配列等が含まれる場合

    クラスを関数内に作成した時にそれを実体コピーさせるreturnで返したいんですが、class内newで形成した動的配列があるため、返した後デストラクタが呼ばれるので動的配列の中身が消滅してしまう(させている)のですが、動的配列の消去にデストラクタを使用しないようにするしかありませんか? class a{ public: int *b; a(){b=new int[10];} ~a(){delete b;} //エラー原因 a operator+(a &s){a c;c.b[0] = b[0] + s.b[0];return c;} }; void main() {a x,y;y = x + x;}

  • new演算子のオーバーロードについて

    #include <stdio.h> #include <windows.h> class MyNew { public: void* ptr; MyNew( void* p ) { ptr = p; } void* MyNew::operator new( size_t size ) { printf("new-\n"); return malloc( size ); } void MyNew::operator delete( void* ptr ) { printf("delete-\n"); free( ptr ); } }; void main( void ) { MyNew p = new int; } クラスのメモリ確保をnew演算子のオーバーロードを用いて書いてみたのですがオーバーロードしたnew演算子が呼ばれません。 なぜでしょうか? /** VisualStdio2005コンソールアプリケーション WindowsXP */

  • C++ 2次元配列について 【 初心者です 】

    こんにちは.C++初心者です. 以下のプログラムは, オブジェクトの2次元配列の作成と そのアクセスをポインタで行うことを 目的としています. 以下の□■部が質問箇所です. なぜobをsamp型でキャストするのか分かりません. obはすでにsamp型で宣言しているのに… それと※部において 2度目のp++処理について教えていただきたいです. メモリーイメージを書いてもらえると ありがたいです。 よろしくおねがいします。 #include <iostream.h> using namespace std; class samp { int a; public: samp(int n) { a = n; } int get_val() { return a; } }; int main(void) { samp ob[3][2] = { 1, 2 3, 4, 5, 6 }; int i; samp *p; // □■□■□■□■ p = (samp *) ob; for(i = 0; i < 3; i++) { cout << p->get_val() << ' '; p++; ※ cout << p->get_val() << endl; ※ p++; } cout << endl; return 0; } }

  • 添字演算子

    #include <iostream> class hoge{ private: int a; public: hoge(){ a = 0; } int operator+(int fuga){ a = a + fuga; return a; } int operator[](int fuga){ return 1; } }; int main(){ hoge* p; p = new hoge; std::cout << ((*p) + 5) << std::endl; std::cout << ((*p)[1] ) << std::endl; // 5 // 1 // std::cout << ((*p) [] 1); エラーです。何故ですか? }

  • メモリ開放の質問ですが

    #include<iostream> using namespace std; void main(void) { int i; char *s,*t; s=new char[9];t=s; for(i=0;i<9;i++)*(s+i)=i; for(i=0;i<9;i++)cout<<(int)s[i]<<endl; delete s; delete t; //error } において2回目のdelete tでエラーになりますが #include<iostream> using namespace std; void main(void) { int i; char *s,*t; s=new char[9];t=s+2; for(i=0;i<9;i++)*(s+i)=i; for(i=0;i<9;i++)cout<<(int)s[i]<<endl; delete t; delete s; } はエラーになりません (s=t+2をs=t+1に変更するとエラーになる) 実際にdeleteは何を行いエラーを引き起こすのでしょうか? エラーになるメカニズムを教えてください

  • newで個別に生成した配列にNULLを代入しても大丈夫か

    C++にて要素数が不定の配列のクラスをnewで生成します。 このとき、newで一度に全ての配列を生成するとメモリをかなり取るので、 指定された要素番号のみを生成したいのでダブルポインタにし、 全体を生成してから個別にクラスを生成することで、 思い通りの処理ができるようになりました。 (生成する番号は0→1→2みたいな順列ではないため) しかし、ここで1つ問題が発生しました。 まずは以下にそのソースを示します。 ----------------------------- class CHOGE {   int a; public:   CHOGE(){ a=999; }   void print(){ cout<<a<<endl; } }; ----------------------------- void main(){   CHOGE **p = new CHOGE*[3];   if( p[0] != NULL ){ // ←条件を通過してしまう     cout << "ほげ~" << endl; // ←表示される     //p[0]->print(); // ←当然エラー   }   p[0] = new CHOGE();   p[0]->print(); // ←OK   delete p[0];   p[0] = NULL;   delete [] p;   p = NULL; } ----------------------------- 上記は3個の要素の配列を生成してから、 個別に0番目の要素のインスタンスを生成しています。 しかし、上記main関数の3行目では、 インスタンスはまだ生成していなにもかかわらず NULLチェックを通り過ぎてしまいます。 そこで上記main関数の2行目に  for(int i=0; i<3; i++) p[i] = NULL; という処理を入れると期待通りに動いてくれるのですが、 どうも引っかかるといいますか、何かの情報を消してしまったり やってはいけないことをやってそうで、不安なのです・・・。 上記のようなとき、NULLを代入しても大丈夫なのでしょうか?

  • new と malloc によるメモリの動的確保について

    初めて投稿させて頂きます。よろしくお願い致します。 現在、以下のような、2次元配列による動的なメモリの確保を意図して、コードをC/C++にて記述しています。 (コンパイラはMINGW32のg++ 3.4.5) //mallocによるメモリ動的確保 data = (char **)malloc(num * sizeof(char*)); for(i=0; i<rowNum; i++){ data[i] = (char *)malloc(sizeof(char)*256); } //newによるメモリ動的確保 data = new char*[rowNum]; for(i=0; i<rowNum; i++){ data[i] = new char; } C++で書くのならば、 「mallocによるメモリ確保は辞め、newによるメモリ確保をしなさい」 という指摘が、書籍でもwebでもありましたので、 両方書き、両者を比べているのですが、理由がイマイチ分かりません。 10万行ほどのテキストデータで実験させてみたのですが、 mallocによる記述の方が、動作が数秒速いようなのです。 それで、new やmalloc で実際何をやっているのか、コードを見ようと思ったのですが、 newでは、 void* operator new(std::size_t) throw (std::bad_alloc); void* operator new[](std::size_t) throw (std::bad_alloc); void* operator new(std::size_t, const std::nothrow_t&) throw(); void* operator new[](std::size_t, const std::nothrow_t&) throw(); inline void* operator new(std::size_t, void* __p) throw() { return __p; } inline void* operator new[](std::size_t, void* __p) throw() { return __p; } というnewファイルの記述で行き詰まり、 malloc は malloc_allocator.hというファイルで行き詰りました。 以上を踏まえて、 1)そもそも、上記のメモリ動的確保記述はスマートな書き方なのか 2)実際に、newやmallocは、どういった手法でメモリ領域を確保しているのか 以上の2点について、ご教授下さい。よろしくお願い致します。

  • デストラクタについて

    #include <iostream> #include <string> using namespace std; #define NUM 2 //登録人数 class Person{ char *name; int *age; char *hobby; public: Person() { name = new char; age = new int; hobby= new char; } void Set(char *n,int a,char *h) { name=n; *age=a; hobby=h; } char *Get_name(void) { return name; } int Get_age(void) { return *age; } char *Get_hobby(void) { return hobby; } ~Person() { cout<<name<<"のデストラクタ\n"; delete [] name; delete age; delete [] hobby; } }; int main(void) { Person *p; int i; p=new Person[NUM]; p[0].Set("永嶋",21,"映画鑑賞"); p[1].Set("平林",54,"車"); for(i=0;i<NUM;i++){ cout<<"\n名前:"<<p[i].Get_name(); cout<<"\n年齢:"<<p[i].Get_age(); cout<<"\n趣味:"<<p[i].Get_hobby()<<"\n"; } delete [] p; return 0; } というプログラムを作成したのですが デストラクタの3つのdeleteがおかしいようなのですが どのような部分が問題となっているのでしょうか? 回答・アドバイス宜しくお願いいたします。

  • int *a = new int; エラー

    下記をコンパイルすると、'int' 型は 'int *' 型に変換できない(関数 main() ) とエラーが出てしまいました。 何がおかしいのでしょうか ? 初歩的な間違いをしていると思いますが、アドバイスお願いします。 #include<iostream> #include<stdlib> #include<ctime> using namespace std; void main(void) { int *a = new int; a=rand(); cout<<a<<endl; delete a; }

専門家に質問してみよう