①ポインタと配列
- 配列
配列を出力するとなると大体こんな風に書く。
#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;において
-
- *pが変数名ではなくpが変数名。つまりpというアドレスを入れる領域を確保する。
- *は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の値が反映されていない
ポインタと配列の違い
そもそもポインタと配列の違いは何なのか?
ここで重要な点を挙げる。
-
- 配列の大きさは要素数*型の大きさである。
- ポインタは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の値はどこからも得られなくなる。
このことをメモリリークと呼ぶ。
上のコードを正常に動作させるには
#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])
で文字列テーブルを使うことが多い。