パソコン活用研究シリコンバレー(C、C++、の活用研究)

再びポインタと配列

 

さて、再びポインタについて、取り上げてみました。
「ポインタでつまづいた人のための・・」という本があるくらいですから、ポインタがCの大きな特徴のひとつで
あることは間違いありません。
BASICにはない概念ですから、BASICでプログラムしていた人には、つまずきやすい点なのかも知れません。
簡単に言ってしまえば、ポインタは変数のアドレスを現しているので、BASICであえて記述すれば
varptr(変数) ということになりますが、Cのポインタは、なんというかかめばかむほどに味のでるもっと味わい
深いものです。、
画面に出力(printf)・キーボードから入力(scanf)・変数  変数の有効範囲、ポインタ、関数のデータ受け渡し
ところでも、簡単にポインタについて触れてますので、お暇でしたら参照して下さい。

1 ポインタの記述の仕方。

何度もかいてますが、ポインタは変数の格納されているアドレスを現します。
普通に  int a; のように変数を宣言した場合、ポインタは &a のように 変数の前に&をつけて現します。
&a はaの格納されているアドレスです。
また、 int *b; のように * をつけて変数をポインタとして宣言することができます。この場合は、
bがポインタ、 *b が変数の値を現します。すなわち、*はそのポインタに格納されている値を現します。

そして、配列の場合、ポインタはもっと便利な道具になります。例えば
int c[3]; のように配列の宣言をした場合、 &c[0]は c[0]のポインタになります。
そして面白いことに、ただcと記述すると、cは配列cの先頭アドレスをさすことになっているので、
&c[0] = c ということになります。また、&c[1]= c+1 ということになります。
逆に、 c[0] = *c, c[1]= *(c+1) です。


例えば、 int c[4] = {0,1,2,3}; と宣言すると、以下のようになります。

変数 C[0]
*C
C[1]
*(C+1)
C[2]
*(C+2)
C[3]
*(C+3)
ポインタ &c[0]
または c
&c[1]
または c+1
&c[2]
または c+2
&c[3]
または c+3

それでは、じっさいのプログラムで確認してみます

/* ポインタ coded by Tsuyoshi Kasai for LSIC */

#include <stdio.h>

main(){
int a,i;
int *b;
int c[3]={1,2,3};
a=1; b=&a;

printf("a:値--> %d &a:ポインタ--> %04x\n",a,&a);
printf("*b:値--> %d b:ポインタ--> %04x\n",*b,b);
for (i=0;i<3;i++){
printf("c[%d]:値--> %d &c[%d]:ポインタ--> %04x\n",i,c[i],i,&c[i]);}  
printf("c:ポインタ--> %04x",c);

変数aは通常の宣言です。 bはポインタとして宣言しました。 cは配列です。
b=&a としていますが、これで、変数bのポインタ=変数aのポインタ  となりました。すなわち、同じアドレス
をさしていることになります。
さて、実行結果です。

C:\C>pointa
a:値--> 1   &a:ポインタ--> 0fc2
*b:値--> 1   b:ポインタ--> 0fc2
c[0]:値--> 1  &c[0]:ポインタ--> 0fc4
c[1]:値--> 2  &c[1]:ポインタ--> 0fc6
c[2]:値--> 3  &c[2]:ポインタ--> 0fc8                 
         c:ポインタ--> 0fc4
C:\C>

a = *b, &a = b, c = &c[0] ですね。
ということで、再度ポインタについて簡単にまとめてみました。
既にBASICにはない世界に踏み込んでいますが、ポインタの味が出るというレベルには程遠いですね。
まだ、ポインタを舐めてみたというところでしょうか。


2 ポインタの加算

上の例で、int c[3]; と配列を宣言した場合、
&c[0] = c, &c[1] = c+1 ・・・となると説明しました。
この時、ちょっと注意しなくてはならないのは、cのポインタに1を加えた時、アドレスはcの型(int型)の分だけ
進むということです。
int型が16bit(=2byte)なら、c+1はcより2byte分進んでいます。
実例を見たほうが分かりやすいと思いますので、以下のプログラムを実行して見てください。

/* ポインタ2 coded by Tsuyoshi Kasai for LSIC */

#include <stdio.h>

main(){
char a[4]="abc";
int i,b[3]={1,2,3};
long c[3]={1,2,3};

for (i=0;i<3;i++){
printf("a+%d--> %04x b+%d--> %04x c+%d--> %04x\n",i,a+i,i,b+i,i,c+i);}    
}

これは、LSICでコンパイルしました。従って、変数aはChar型(1byte),bはint型(2byte)、
cはlong型(4byte)となっています。
以下の実行例を見てください。それぞれのポインタの進みかたに注意して下さい。

C:\C>pointa2
a+0--> 0f76  b+0--> 0f70   c+0--> 0f64                   
a+1--> 0f77  b+1--> 0f72   c+1--> 0f68
a+2--> 0f78  b+2--> 0f74   c+2--> 0f6c

これは、ポインタ=変数の格納されているアドレス とは言っても、Basicのvarptr(変数)と単純に同じ
ということではないということに、注意してください。ポインタの方が使いやすくできていることがわかると
思います。


3 2次元配列

2行*3列の配列

[0][0] [0][1] [0][2]
[1][0] [1][1] [1][2]

例えば上のような、2行*3列の配列は、以下のように宣言します。
int data[2][3] = { {0,1,2},
            {3,4,5}};


この配列は、メモリーの連続した領域に次のような順番で作られます

[0][0] [0][1] [0][2] [1][0] [1][1] [1][2]

2次元配列で、ただ data と書くと data[0][0] のアドレスをさします。
すなわち、 data = &data[0][0]  です
また、 data[0] は data[0][0] のアドレスをさし、data[1] は data[1][0] のアドレスをさします。
data[0]+1 は data[0][1] のアドレスです。
すなわち、data = data[0] = &data[0][0] data[1] = &data[1][0]  data[0]+1 = &data[0][1] です

それでは、*dataはなにものでしょうか、1次元配列から類推すると、 *data = data[0] ですが、
どうでしょうか。じつは、その通りです。従って、
data = *data = data[0] = &data[0][0] ということになります。 

ここまでは、わりとわかりやすかったと思います。それでは、data+1 は何をさすでしょうか。
単純に、 data+1 = data[0]+1 = &data[0][1]
と考えてしまいがちですが、おおはずれです。ここらへんは、ポインタがかめばかむほど味がでる部分でも
あります。人によっては、ちょっと苦い味と感じるかもしれませんが。
実際は、 data+1 = data[1] =&data[1][0] です。

それでは、data+1, *(data+1), *data+1 はどうでしょう。全て同じ物をっさすのでしょうか。
少し、頭の中が混乱してきたかもしれませんが、あわてずに考えて見てください。
data+1 = data[1] でした。 一方、 data = *data = data[0] ですから、 *data+1 は data[0]+1 と同じ
になります。違いがわかりましたか。 ちなみに、*(data+1) は data+1 と同じです。

結構、ごちゃごちゃしてきましたので、表にしてまとめてみましょう
2次元配列のアドレス(ポインタ)の記述の仕方です。

[0][0]
&data[0][0]
data[0]
*data
data
[0][1]
&data[0][1]
data[0]+1
*data+1
[0][2]
&data[0][2]
data[0]+2
*data+2
[1][0]
&data[1][0]
data[1]
*(data+1)
*data+3
data+1
[1][1]
&data[1][1]
data[1]+1
*(data+1)+1
*data+4
[1][2]
&data[1][2]
data[1]+2
*(data+1)+2
*data+5

次は、2次元配列の値を記述する方法にいってみましょう。
data[0][0] = *data[0] ですね。
さて、上のポインタのところで、 data[0] = *data という関係がありましたが、これから考えると
data[0][0] = *data[0] = **data となりそうです。これは、正しいでしょうか。
実は正解です。Cでは、こういう記述の仕方ができます。

では、data[0][1] は他にどう記述できるでしょうか。
data[0][1] = *(data[0]+1) = *(*data+1) です。
また、data[1][0] = *data[1] = **(data+1) = *(*data+3) です。

もう、おわかりだと思いますが、**data+1, *(*data+1), **(data+1) はみな異なる変数です。
**data+1 = data[0][0]+1
ということですから。

それでは、2次元配列の値についても、表を作ってみましょう。

[0][0]
data[0][0]
*data[0]
**data
[0][1]
data[0][1]
*(data[0]+1)
*(*data+1)
[0][2]
data[0][2]
*(data[0]+2)
*(*data+2)
[1][0]
data[1][0]
*data[1]
**(data+1)
*(*data+3)
[1][1]
data[1][1]
*(data[1]+1)
*(*data+4)
[1][2]
data[1][2]
*(data[1]+2)
*(*data+5)

以下、実際にプログラムで実験してみました。実行結果と合わせて見て確認して見てください。
3行*3列の配列です。

/* nijigen0.c   Coded by Tsuyoshi Kasai  for LSIC     */
/*  2次元配列の実験  */
#include <stdio.h>
int data[3][3] = {{0,10,20},
                  {30,40,50},
                  {60,70,80}};
main(){
  int i;  
 for (i=0;i<6;i++)
printf("*data+%d:%4d, data[0]+%d:%4d\n",i,*data+i,i,data[0]+i);  
printf("\n");

  for (i=0;i<6;i++)
printf("**data+%d:%2d, *(*data+%d):%2d, *(data[0]+%d):%2d \n"
                ,i,**data+i,    i,*(*data+i),     i,*(data[0]+i));
printf("\n");

  for(i=0;i<3;i++)
printf("data+%d:%4d, *(data+%d):%4d, data[%d]:%4d\n"
             ,i,data+i      ,i,*(data+i),  i,data[i]); 
printf("\n");

  for (i=0;i<3;i++)
printf("**(data+%d):%2d, *data[%d]:%2d \n"
               ,i,**(data+i),   i,*data[i]);      
  }
                  
C:\C>nijigen0
*data+0: 102, data[0]+0: 102
*data+1: 104, data[0]+1: 104
*data+2: 106, data[0]+2: 106
*data+3: 108, data[0]+3: 108
*data+4: 110, data[0]+4: 110
*data+5: 112, data[0]+5: 112

**data+0: 0, *(*data+0): 0, *(data[0]+0): 0
**data+1: 1, *(*data+1):10, *(data[0]+1):10
**data+2: 2, *(*data+2):20, *(data[0]+2):20
**data+3: 3, *(*data+3):30, *(data[0]+3):30
**data+4: 4, *(*data+4):40, *(data[0]+4):40
**data+5: 5, *(*data+5):50, *(data[0]+5):50     

data+0: 102, *(data+0): 102, data[0]: 102
data+1: 108, *(data+1): 108, data[1]: 108
data+2: 114, *(data+2): 114, data[2]: 114

**(data+0): 0, *data[0]: 0
**(data+1):30, *data[1]:30
**(data+2):60, *data[2]:60
             

 

TopPage