ページ 11

C++のnew演算子とポインタについてのプログラムなんですが・・・

Posted: 2012年4月26日(木) 01:18
by ノクト
C++のnew演算子とポインタについてのプログラムで
3次元配列の動的確保を行うプログラムを作成したんですが、
イマイチ内容がピンときません。プログラムの一部は以下のように
なってるんですが。

コード:

int ***p;
int i,j;

p=new int**[2];[tab=30][tab=30]//まずなぜここで**p=・・・とならない?
[tab=30]for(i=0;i<2;i++){
[tab=30][tab=30]p=new int*[2];//上のように*p=・・・になぜならない?
[tab=30][tab=30]for(j=0;j<2;j++){
[tab=30][tab=30][tab=30]p[j]=new int[2];
[tab=30][tab=30]}
[tab=30]}

とネットで調べた3次元配列のプログラムなんですが、
なぜポインタのポインタのポインタで宣言したのに
動的確保を行っていくと普通の3次元配列になっているのか
どのサイトを見ても図で説明したりしてくれてるサイトが
ないのでポインタや配列の状態がイマイチピンときません。
どなたか詳しく教えてください。

Re: C++のnew演算子とポインタについてのプログラムなんですが・・・

Posted: 2012年4月26日(木) 03:56
by kurain
javaと同じなのかな?
だとすれば

コード:

ポポポ
イイイ
ンンン
タタタ
。のの
■

■→●●(ポインタのポインタ)

   ★(ポインタ)
   ★(ポインタ)
   ↑
■→●●
  ↓
  ★(ポインタ)
  ★(ポインタ)

   ★→(int配列)
   ★→(int配列)
   ↑
■→●●
  ↓
  ★→(int配列)
  ★→(int配列)
ってことでしょうか?

Re: C++のnew演算子とポインタについてのプログラムなんですが・・・

Posted: 2012年4月26日(木) 06:29
by beatle
良い図を書くのは難しいので、ちょっとしたクイズ形式の文章にしてみました。

次のプログラムは納得できますか?

コード:

int* p_i;
p_i = new int[100];
単にint型の配列を確保しているだけですね。メモリ上に「int型」の変数が100個並びます。

では、次のプログラムはどうでしょう。

コード:

TypeB* p_b;
p_b = new TypeB[100];
intがTypeBに置き換わっただけですから、納得できると思います。
TypeBは実際には何かのtypedefだと思って下さい。
メモリ上に「TypeB型」の変数が100個並びます。


さて、TypeBを以下のように定義してみましょう。

コード:

typedef int* TypeA;
typedef TypeA* TypeB;
こう定義すると、ポインタp_bはint型へのポインタへのポインタへのポインタ、ということになります。
しかし、変数定義は2番目のプログラムと変わることはなく、TypeB* p_b; p_b = new TypeB[100];のままです。

Re: C++のnew演算子とポインタについてのプログラムなんですが・・・

Posted: 2012年4月26日(木) 08:24
by bitter_fox
ノクト さんが書きました:C++のnew演算子とポインタについてのプログラムで
3次元配列の動的確保を行うプログラムを作成したんですが、
イマイチ内容がピンときません。プログラムの一部は以下のように
なってるんですが。
型を意識すればわかると思います。
まず、変数pがint***として宣言されている場合は、pの型はint***、*pの型はint**、**pの型はint*、***pの型はintというのは理解できてますよね。

そして、new int**[2]の型はint***となります。なので代入先となるのは同じint***のpとなるべきです。
一方**pとするとint*の型として代入しなければならず、誤りであるのが理解できると思います。

同様に

コード:

p[i]=new int*[2];
もint*[2]がint**型で、pがint**型なので整合性が取れています。
左辺値を*pとするとint*型となってしまうので、誤りとなります。

ノクト さんが書きました:とネットで調べた3次元配列のプログラムなんですが、
なぜポインタのポインタのポインタで宣言したのに
動的確保を行っていくと普通の3次元配列になっているのか
どのサイトを見ても図で説明したりしてくれてるサイトが
ないのでポインタや配列の状態がイマイチピンときません。
どなたか詳しく教えてください。

アクセスの仕方は普通の三次元配列と酷似していますが、実態はかなり異なった構造になっています。

まず、普通の三次元配列の構造を図として示します。(プログラムに合わせて2×2×2の配列)
配列.png
配列.png (5.23 KiB) 閲覧数: 2429 回
(メモリAddrの値はあくまで目安です。)
注目すべき点は配列の場合はすべての要素が連続してメモリ上に配置されるところです。

次に、2×2×2となるように動的確保した物の構造です。
動的確保.png
動的確保.png (21.67 KiB) 閲覧数: 2429 回
配列とは違い、すべての要素が連続することはありません。

配列は配列のサイズをカギにアクセスしていきますが、動的確保した物はメモリアドレスの値をカギにアクセスしていきます。
[hr][追記]アクセスについての例
上の図の場合に、三次元配列aryに対してary[1][1]とするとその値は1006となります。
これは、aryが1000となるのを基準として、二次元目の大きさ、一次元目の大きさを考慮して計算します。
まず、一次元目の大きさは2となります。
そして、それが2個あるのが二次元目なので、二次元目の大きさは2 * 2の4となります。
つまり、ary[1][1]は
ベースアドレス + 1 * 二次元目の大きさ + 1 * 一次元目の大きさ = 1000 + 1 * 4 + 1 * 2 = 1006
と計算されます。

次にpに対して、p[1][1]とするとその値は7000となります。
これは結構普通に算出されます。
まず、pの値(アドレス値)は1000です。
そして、p[1]は*(p + 1)であり、pの値を考慮すると*(1001)となるのでその値は3000です。
同様に、p[1][1]は*(p[1] + 1)なので、*(3001)となりその値は7000となります。

このように構文上のアクセスの仕方は同じでも、多次元配列と動的確保されたものでは全く計算方法が違います。

[編集]p[1][1]の計算方法について編集。