電脳ミツバチのコンピュータ広報室

銀座の屋上菜園を耕しています。コンピュータ畑も耕します。

①ポインタと配列

スポンサーリンク
  • 配列

配列を出力するとなると大体こんな風に書く。

#include<stdio.h>
#define size 10
main()
{
	int i;
	int array[size]={232,21,4,142,412,532,643,745,12,543};
	for(i=0;i<size;i++)
		printf("%d\n",array[i]);

}

ではこの時メモリ上ではどうなっているのだろうか?
%pで表してみると

#include<stdio.h>
#define size 10
main()
{
	int i;
	int array[size];
	for(i=0;i<size;i++)
		printf("0x%p==array[%d]\n",&array[i],i);
	printf("size=%d\n",sizeof(array));
}

結果
0x0012FF64==array[0]
0x0012FF68==array[1]
0x0012FF6C==array[2]
0x0012FF70==array[3]
0x0012FF74==array[4]
0x0012FF78==array[5]
0x0012FF7C==array[6]
0x0012FF80==array[7]
0x0012FF84==array[8]
0x0012FF88==array[9]
size=40

型の項でやったようにint型のサイズは4バイトでした。
ですから4バイトずつとってarray[9]の0x0012FF88からarray[1]の0x0012FF64を引くと
16進数で22、10進数で36。array[9]も4バイト持っているので36+4で40バイト。
sizeof(array)のsizeと一致します。
もちろんchar型では一バイトずつとっていきます。

#include<stdio.h>
#define size 10
main()
{
	int i;
	char array[size];
	for(i=0;i<size;i++)
		printf("0x%p==array[%d]\n",&array[i],i);
	printf("size=%d\n",sizeof(array));
}

結果
0x0012FF80==array[0]
0x0012FF81==array[1]
0x0012FF82==array[2]
0x0012FF83==array[3]
0x0012FF84==array[4]
0x0012FF85==array[5]
0x0012FF86==array[6]
0x0012FF87==array[7]
0x0012FF88==array[8]
0x0012FF89==array[9]
size=10

こちらで説明したほうが分かりやすかった。
sizeが10あるというのが簡単に分かる。

  • ポインタ

続いてポインタの覚えるべき点を記述。
int *p;において

    1. *pが変数名ではなくpが変数名。つまりpというアドレスを入れる領域を確保する。
    2. *はpがポインタ変数であることを示す演算子です。

ポインタと変数の関係は以下の通りである。

#include<stdio.h>
main(){
	int a;
	int *p;
	a=10;
	p=&a; //*アドレス←アドレス*//
	printf("%d\t",a);
	printf("%d\n",&a);
	
	printf("%d\t",p);
	printf("%d\n",*p);
}

結果
10      1245064
1245064 10

要するにアドレスを記述するのに変数なら「&」aと記述しポインタなら「」pとなにも付けない。
値を記述するなら変数なら「」aをポインタなら「*」pと記述する。
代入には上記のようにアドレス←アドレスと代入してもいいし、
値←値(*p=a;)と代入しても値に関しては10という結果が得られる。

#include<stdio.h>
main(){
	int a;
	int *p;
	a=10;
	*p=a;
	

	printf("%d\t",a);
	printf("%d\n",&a);
	
	printf("%d\t",p);
	printf("%d\n",*p);
}

結果
10      1245064
2147344384      10

だが値←値ではただの代入であってaの値を変更しても*pの値は変わらない。
逆に言うとアドレス←アドレスによって結びつけることでaの値を変更すると*pにも反映されるというのがポインタの肝である。

#include<stdio.h>
main(){
	int a;
	int *p;
	a=10;
	p=&a;   //*アドレス←アドレス*//
	a=a+10;

	printf("%d\t",a);
	printf("%d\n",&a);
	
	printf("%d\t",p);
	printf("%d\n",*p);
}
結果
20      1245064
1245064 20   aの値の変更が*pに反映された様子。

	
#include<stdio.h>
main(){
	int a;
	int *p;
	a=10;
	*p=a;    //*値←値*//
	a=a+10;
	printf("%d\t",a);
	printf("%d\n",&a);
	
	printf("%d\t",p);
	printf("%d\n",*p);
}

20      1245064
2147348480      10	←*pの値が反映されていない

ポインタと配列の違い

そもそもポインタと配列の違いは何なのか?
ここで重要な点を挙げる。

    1. 配列の大きさは要素数*型の大きさである。
    2. ポインタは4バイトしかない
    3. 配列は、メモリ位置を変更することは出来ない。
    4. ポインタは好きなメモリアドレスを指せる。

ポインタが可変である証拠に面白いコードがある。

#include<stdio.h>
main(){
	int i;
	char array[]="0123456789";
	char *p;
	p=&array[5];
	for(i=-5;p[i]!=0;i++)
		printf("p[%d]=%c\n",i,p[i]);
}

p[-5]=0
p[-4]=1
p[-3]=2
p[-2]=3
p[-1]=4
p[0]=5
p[1]=6
p[2]=7
p[3]=8
p[4]=9

このように必ずしもポインタは[0]から値をとる必要すらない。
アドレスを自由に動かせるのである。
そしてその演算は殆ど(アドレス)±(型のバイト数)か
配列のように[]の中に要素を入れることで動かし得る。

ポインタと配列の複合

ポインタのポインタにアドレスのアドレスをいれてprintfで値の値を出力すれば元の値と同じになる。
とかくと何のことか分からない。私も混乱した。
実際にコードを見てもらったほうが良い。

#include<stdio.h>
main(){
	int ***ppp;
	int **pp;
	int *p;
	int  a=1234;
	p=&a;
	pp=&p;
	ppp=&pp;
	printf("a=%d,\n*p=%d,\tp=%d\n**pp=%d,\t*pp=%d,\tpp=%d\n***ppp=%d,\t**ppp=%d,\t*ppp=%d,\tppp=%d\n",a,*p,p,**pp,*pp,pp,***ppp,**ppp,*ppp,ppp);	
}

結果
a=1234,
*p=1234,        p=1245056
**pp=1234,      *pp=1245056,    pp=1245060
***ppp=1234,    **ppp=1245056,  *ppp=1245060,   ppp=1245064

こんな具合である。
さらにアドレスなので4バイトずつきっちり取っているところにも注目したい。
非常に面白いのだが残念ながら使い道があまりない。
数少ない使いどころはmallocしたアドレスを戻すときとかに使える。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N	10

void memoryalloc(char *p1)
{
	p1=malloc(20);
	strcpy(p1,"testtesttest");
}
	
main(){
	char *carray="ABCDEFGH";
	memoryalloc(carray);
	printf("carray=%s\n",carray);
}
結果
carray=ABCDEFGH

おかしいのに気付くだろうか?
strcpyでp1にtesttesttestという文字列をp1に入れているのにもかかわらずcarrayの値は変わっていない。
この原因を探る。
関数の引数というのは引数のもつアドレスが渡される。
ここまではいい。
従ってポインタのアドレスがp1ということになる。
mallocで得られた値はアドレスのアドレスを書き換えているのである。

                          • -
| p1 | p1をstrcpyで書き換える。
                          • -
↓*carrayのアドレス
                                              • -
|*carray|
                                                • -
  ↓carrayのアドレス
                                              • -
|carrayのデータ|
                                              • -

分かるだろうか?
p1の値はどこからも得られなくなる。
このことをメモリリークと呼ぶ。
上のコードを正常に動作させるには

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void memoryalloc(char **p1)
{
	*p1=malloc(123);
	strcpy(p1,"testtesttest");
}
	
main(){
	char carray[]="ABCDEFGH";
	memoryalloc(carray);
	printf("carray=%s\n",carray);
}
結果
carray=testtesttest

こうしてやればよい。


最後にこういった文字列を複数扱うときにはポインタの配列(例;*pa[4])
で文字列テーブルを使うことが多い。