ポインタの説明
変数名とアドレスで、確保されるメモリアドレス(番地)を表示する【 &a 】「例:(printf”aのアドレス%p\n”,&a);」これで変数名【 a 】のアドレスを出力させることが出来ます。出力されるアドレスは特殊な為、通常の変数に代入することは出来ません。そこでアドレス値を格納できる専用の変数が用意されています。それがポインタといわれるものです。
ポインタは以下の様に宣言します。
例:【 int *a; 】
例:【 int *adc, *def; 】
任意の変数名の前に【 * 】アスタリスクが付き、これらのポインタに変数のアドレスを代入させることが可能になります。
例、以下のように変数【 a 】に確保されたメモリアドレスの値をポインタ宣言した変数【 p 】に代入する事が可能になります。
サンプルソース
#include <stdio.h> int main(void) { int *abc; int a = 123; abc = &a;printf(“aの値%d\n”,a); printf(“アドレス%p\n”,%a); printf(“ポインタabcのアドレス%p\n”,abc); return 0; } |
解説、変数【 a 】に確保されたアドレスの値を、ポインタ【 abc 】に代入し、それぞれ出力させております。
出力結果
ポインタによる値の表現
ポインタが変数のアドレスを保持しているなら、そのアドレスに確保している値を取得できます。
ポインタで値を指定するサンプルソース
#include <stdio.h> int main(void) { int *po; int da = 100; int ida; po=&da; ida=*po; printf(“変数daの値%d\n”,da); printf(“ポインタpoの値%d\n”,*po); printf(“変数idaの値%d\n”,ida); return 0; } |
解説、アドレスに確保されている値を出力するには、変換文字列【 %d 】を使います。
変数【 da 】のメモリアドレスを、ポインタ【 po 】に代入した上、変数【 ida 】にはポインタで示す値を代入させてます。これにより出力される値は、それぞれ【 da 】の初期値 100 が出力されます。
実行結果
ポインタと変数の関係について
ポインタと変数の関係に付いてわかりやすく説明させて頂きたいと思います。
例【 メモリアドレス0 】に確保された変数【 da 】のアドレスを取り出す場合は
【 *po = &ida; 】(前回まで勉強してまいりました。)
極端ですが・・・【 printf(“%p\n”,*po); 】とすれば出力結果に【 メモリアドレス0 】と出力されます。
メモリアドレス0 | メモリアドレス4 | メモリアドレス5 | メモリアドレス6 | メモリアドレス7 |
da |
※ポインタ宣言は【 int *po; 】とします
※int型とします。int型の場合は4バイト消費。
以下メモリアドレス表内も同じ
変数に代入された値を、メモリアドレスを指定して出力する場合
変数【 da 】に値を代入します。例【 da = 123; 】
変数のアドレスをポインタに代入します。【 po = &da; 】これでポインタに変数【 da 】のアドレス(メモリアドレス0)が代入された事になります。
(メモリアドレス0)を指定して出力したい場合
【 printf(“%d\n”,*po); 】とすれば出力結果に【 123 】と出力されます。
メモリアドレス0 | メモリアドレス4 | メモリアドレス5 | メモリアドレス6 | メモリアドレス7 |
da 【 123 】 |
メモリアドレスに代入された値を変更
変数【 da 】に値を代入します。【 da = 123; 】
変数【 da 】のメモリアドレスをポインタ【 *po 】に代入。【 po = &ad; 】
変数の値を変更する場合は
【 da = 456; 】とする場合と【 *po = 456; 】とすれば(メモリアドレス0)の値は【 456 】に書き換えられます。
メモリアドレス0 | メモリアドレス4 | メモリアドレス5 | メモリアドレス6 | メモリアドレス7 |
da 【 123 】 ↓ 【 456 】 |
上記で説明したサンプルを読んで頂くと下サンプルが判りやすく理解できると思います。
サンプルソース
#include <stdio.h> int main(void) { int *po; int da = 123;po = &da;printf(“初期値の値%d\n”,da); printf(“初期値の値%d\n”,*po);da = 456; printf(“変更された値%d\n”,da); printf(“変更された値%d\n”,*po);*po = 789; printf(“変更された値%d\n”,da); printf(“変更された値%d\n”,*po);return 0; } |
解説、変数の初期値をそれぞれ、変数【 da 】・ポインタ【 *po 】による値の変更確認です。
出力結果、変更された同じ値が出力されます。
配列とポインタ
ポインタと変数のメモリアドレスの関係を説明させて頂きました。次は配列(例【 a[3] 】)をポインタで操作する方法を勉強してみたいと思います。
配列をポインタで操作する際には、配列特有のポインタ機能があります。
サンプルソースコード
int a[3]; int po; |
上記の変数をポインタでそれぞれ取得するには
※a[5]と配列宣言すると、【 a[0] 】~【 a[2] 】までの変数が用意されます。
po = &a[0]; po = &a[1]; po = &a[2]; |
配列のアドレスを確認するサンプルソースコード
#include <stdio.h> int main(void) { int a[3] = {10, 20, 30,}; int *po;po=&a[0]; printf(“値%d \n”,*po); printf(“アドレス%p \n”,po);po=&a[1]; printf(“値%d \n”,po); printf(“アドレス%p \n”,po);po=&a[2]; printf(“値%d \n”,*po); printf(“アドレス%p \n”,po);po=a; printf(“値%d \n”,*po); printf(“アドレス%p \n”,po);return 0; } |
解説、ポインタに配列のアドレスをそれぞれ代入し、ポインタでそれぞれ「値」・「メモリアドレス」を出力。
※【 po=a; 】としている記述があります。これは【 po=&a[0]; 】と同じ意味です。
実行結果
ポインタ 加算と減算
ポインタの演算は、配列要素(※配列の長さ)を順にたどっていくために使用する方法です。
※配列要素(配列の長さ)
例:【 a[5]; 】と配列宣言すると、【 a[0] 】~【 a[4] 】の変数が用意されます。この範囲を配列要素と言います。
※2、ポインタに対しては次の演算しだけが使用できることになっています。
【 + - ++ – 】インクリメントやデクリメントの様な感じです。
ポインタでの演算記述サンプルです。減算も可能ですが、事実上加算にて利用する事が殆どとの事です。サンプルは加算のみのサンプルとなります。
#include <stdio.h> int main(void) { int ad[4] = {100, 200, 300, 400}; int *po;po=&ad[0];while (1) { printf(“ポインタ値=%d\n”,*po); if(*po == 400) break; ++po; }return 0; } |
解説、ポインタをインクリメントし、【 po=&a[0] 】に代入されたアドレスを一づつ後ろにたどって出力する事が出来ます。
配列a[0]~a[3]までのアドレスが以下の用に確保されているとしますと
ポインタ【 po 】に【 a[0] 】のアドレスを代入し【 ++po 】で加算していくと、メモリアドレスをたどって出力されていく事になります。
メモリアドレス0 【 po 】 |
メモリアドレス4 【 po+1 】 |
メモリアドレス8 【 po+2 】 |
メモリアドレス12 【 po+3 】 |
a[0] | a[1] | a[2] | a[3] |
※int型のメモリ領域は4バイト確保される為、上記例のメモリアドレスは4づつ繰り上げて表示しております。
※2、無限ループに付いては左下メニューのこれまでのまとめをご参照お願い致します。
実行結果。
デクリメントで説明致します。ポインタに配列アドレス【 a[3] 】のアドレスを代入して【 –po 】デクリメントしてみました。
※ループに付いては、【 a[3] 】から順番に出力されるので【 a[0] 】の初期値100での【 break 】終了としております。
#include <stdio.h> int main(void) { int ad[4] = {100, 200, 300, 400}; int *po;po=&ad[3];while (1) { printf(“ポインタ値=%d\n”,*po); if(*po == 100) break; –po; }return 0; } |
実行結果。
文字列をポインタで処理する方法
文字列を扱う注意点は、文字列の終了を知らせる【 ”\0″ 】が必ず入る点に注意しなくては行けません。まずは簡単な産婦るを見て頂いて、前回勉強しましたポインタの加減算と同じ方法で処理する方法を見てみたいと思います。
【 int 】(整数)型と【 char 】(文字列)型変数のメモリ確保容量と配列の違いを見てみる
※【 int 】型は4バイト使い、【 char 】型は1バイト使います。
※2、【 char 】型の場合は文字終端マーク”\0″が必ず代入されます
下記は文字列と整数型変数宣言とメモリアドレス領域を表して見たものです。
例:【 char a[2]=”AB”; 】
例:【 int b[2]={10,20,30}; 】
とそれぞれした場合
メモリ領域 | アドレス0 | アドレス1 | アドレス2 |
char a[2]の場合 | A a[0] |
B a[1] |
\0 a[2] |
メモリ領域 | アドレス0 | アドレス4 | アドレス8 |
int b[2]の場合 | 10 b[0] |
20 b[1] |
30 b[2] |
※メモリ確保領域に付きましては、まだ詳しく覚える必要はないと思いますが、確認の為表にての確認をさせていただきました。
文字配列をポインタで出力サンプル。
配列とポインタの関係を簡単にまとめてみました。
※一番最初の【 chp=ch; 】ポインタにchのアドレスを代入しているのですが、通常は【 chp=&cd[0] 】とするのですが、配列の先頭アドレスをポインタに代入する場合のみ、【 chp=ch; 】と【 & 】を省略する事ができます。
※【 chp=&ch; 】とするとコンパイルエラーになります。
#include <stdio.h> int main(void) { char ch[5]=”ABC”; char *chp;chp=ch;printf(“%s\n”,ch); printf(“%s\n”,chp);chp=&ch[1]; printf(“%c\n”,ch[1]); printf(“%c\n”,*chp);chp=&ch[2]; printf(“%c\n”,ch[2]); printf(“%c\n”,*chp);return 0; } |
解説、文字列変数宣言【 char 】を使用した場合、ポインタ宣言も【 char 】(例:char *chp;)とします。
配列【 ch[5] 】を用意し、それぞれABCを初期値に設定。
※特に【 ch[4] 】としてもOKです。文字列の場合終端マークに0が入るので、余分に配列を用意しております。
上記サンプルにも書きましたが、配列の先頭アドレスをポインタに代入する場合【 & 】と要素を付加しなくていいとの事です。
それぞれ配列要素にてポインタを更新し、それぞれの文字が同じ物が正しく出力されているかを見ております。
実行結果
ポインタを更新せずに、加算して文字配列の要素をそれぞれ出力するサンプルです。
#include <stdio.h> int main(void) { char ch[5]=”ABC”; char *chp;chp=ch;printf(“配列の先頭アドレス %c\n”,*chp);printf(“次の要素をアドレス %c\n”,*(chp+1));printf(“その次の要素アドレス %c\n”,*(chp+2)); return 0; |
解説、ポインタを加算することにより、先頭アドレス以降に代入されている文字がそれぞれ出力されます。
ポインタ【 chp 】に配列【ch[0]】の先頭アドレスが代入されているので、ポインタ+1で配列要素【 a[1] 】が出力され、
+2にすると配列要素【 a[3] 】が出力されます。
但し、先のサンプルのようにポインタを更新の上出力しているわけではありませんので、ポインタに代入されているのは先頭アドレスのままです。
ポインタを加算する場合は【 *(ポインタ名+1) 】としないといけません。【 putchar 】を用いてポインタを加算の上出力する場合でも同様です。例【 putchar(*(chp+1)); 】等とします。
実行結果
変数名とアドレスについて
コンピューターの処理は、メモリ上に何らかのデーターを置いて処理を進めます。コンピューターのメモリには、各データを識別する為に、先頭から順番に、0からの連続番号が付けられております。これをアドレスと言います。(番地とも表現します)
メモリ上のアドレス(番地)イメージ
メモリアドレスはそれぞれ1バイト単位で確保されています。
0番地 | 1番地 | 2番地 | 3番地 | 4番地 |
1バイト | 1バイト | 1バイト | 1バイト | 1バイト |
例えば、3番地に10と言う数字をメモリ上に置く場合は【 3番地に10を入れます 】と表現します。ただ扱うデーターが沢山あると、0番地・1番地、、とアドレスを指定するのは不便です。
そこで、番地の事を変数で表現すれば便利に使えると言う事で【 変数 】と言う名前付けルールが考えられました。
例えば 【 int a=10; 】とすると、変数【 a 】と言う番地に【 10 】と言うデータを置くと言う意味になります。
※【 int 】型はデーター1個が4バイトで構成されます。
※2、確保されるアドレスは「リンカ」によって決められます。
0番地 | 1番地 | 2番地 | 3番地 | 4番地 |
10 | 1バイト |
例2:【 int a=10; char b=20; 】とすると、変数【 a 】には【 10 】、変数【 b 】と言う番地に【 20 】と言うデーターを置くことができます。
※【 char 】型データー1個が1バイトで構成されます。
0番地 | 1番地 | 2番地 | 3番地 | 4番地 |
10 | 20 |
変数のサンプルソース
#include <stdio.h> int main(void) { int a=10;printf(“aの値%d\n”,a); printf(“aのアドレス%p\n”,&a); return 0; } |
解説図
メモリアドレス出力記述【 printf(“aのアドレス%p,&a”); 】に注目して下さい。アドレス変換出力させるには、変数の前に【 & 】を付けます。変換仕様は【 %p 】になります。
出力結果
コメント