トップページへ

C言語のポインターについて

Access Count : 1789

Copyright © 2019 TAKEHANA TADASHI
著作日時: 2020.08.14.金. 19:49:00 著作者、竹花 忠
 ポインターということばからの印象としては、ある1点を指し示すもの、みたいな意味に取れる。
 コンピュータープログラミング言語にC言語がある。
 C言語には、ポインターがある。
 C言語の学習では、ポインターで躓く(=つまずく)ひとが多いようである。
 具体的には、int型のポインター変数とかchar型のポインター変数とかがある。
 ポインター変数をどう設定して、どう使用して、値を取得するか。
 C言語を学習していると、それらについての理解が十分に達せられないひとが出てくるようである。
 その原因のひとつは、名称から喚起させられること以外のことまでも含めて把握しないと、ポインターというかポインター変数というかについては、十分な理解とはならないこと。
 つまり、ある1点を指し示す値が格納されていてその値を使用してその位置に格納されている値を取得するのがポインター変数である、というくいらのことまでなら、ポインターということばから喚起させられる印象と結び合わせやすい。
 実際、その結び合わせで、ポインター変数の1/5くらいの理解にはなっている。



 ただ、指し示した位置に格納されている値を取得すると言っても、どの型のデーターを取得するのかによって、指し示した位置から何ビットにわたってのデーターを1個のデーターとして取得するかが異なってくるという問題がある。
 なので必ずポインター変数は、どの型のデーターを取得するポインター変数であるのかを明示して、宣言・定義、する。
 char型のデーターを取得するポインター変数なら、
char *phesuh;
とか、int型のデーターを取得するポインター変数なら、
int *pointer;
とか、言うように、ポインター変数名の前に、取得するデーターの型、空白、そして、*を置き、そして変数名の後には;を置く。
 つまり、
char *phensuh;
int *pointer;
のようにポインター変数は、宣言・定義、する。
 char型のデーターのサイズは、8ビットである。int型のデーターのサイズは、32ビットであるとする。
 ちなみに、8ビットは1バイトと呼ばれる。
 そして、コンピューターのメモリー番地は、1バイト刻みで、1カウントずつアップしてゆく。



 150000番地からにあるchar型のデーターが取得したいなら、
char *phensuh;

phensuh = (char *)150000; //150000が、char型のポインターの値であることを明示している。
printf("%d\n",phensuh); //phensuhの値、150000を表示。
printf("%c\n",*phensuh); //150000番地に格納されている文字コードに対応した文字を表示。
 つまり、*phensuhによって、phensuhに格納されている値をメモリー番地として、そのメモリー番地からchar型の値が取得される。当然、char型のサイズで。

 150000番地からにあるint型のデーターが取得したいなら、
int *pointer;

pointer = (int *)150000; //int型のポインターの値であることを明示している。
printf("%d\n", pointer); //pointerの値、150000を表示。
printf("%d\n",*pointer); //150000番地に格納されている数値を表示。
150000番地からの32ビットのデーターが10進数で表示される。
 つまり、*pointerによって、pointerに格納されている値をメモリー番地として、そのメモリー番地からint型の値が取得される。当然、int型のサイズで。

 ちみなに、メモリー番地は、8ビット刻みで1カウント増えてゆく。



 なお、
phensuh = phensuh + 1; //phensuhの中身は、150001になる。
printf("%d\n", phensuh); //150001が表示される。
printf("%c\n",*phensuh); //150001番地からの8ビットのchar型のデーターが表示される。
 char型のポインター変数に1を加えると、char型のデーターサイズ1個分増えたところのメモリー番地になる。

pointer = pointer + 1; //pointerの中身は、150004になる。
printf("%d\n", pointer); //150004が表示される。
printf("%d\n", *pointer); //150004番地からの32ビットのint型のデーターが取得される。
 int型のポインター変数に1を加えると、int型のデーターサイズ1個分増えたところのメモリー番地になる。つまり、int型のポインター変数に1加えると、その中身の値は、加えた値の4倍の量だけ増える。



 int型の配列の名前は、その配列の先頭のメモリー番地を保持したint型のポインター定数である。
 なので、それをint型のポインター変数に代入することで、int型の配列の先頭番地を、int型のポインター変数に設定できる。

 char型の配列の名前は、その配列の先頭のメモリー番地を保持したchar型のポインター定数である。
 なので、それをchar型のポインター変数に代入することで、char型の配列の先頭番地を、char型のポインター変数に設定できる。

 つまり、
int h[2] = {12, 15}; //int型の配列の、宣言・定義。
char c[5] = {'a','b','c','d','e'}; //char型の配列の、宣言・定義。

char *phensuh; //char型のポインター変数の、宣言・定義。
int *pointer; //int型のポインター変数の、宣言・定義。

phensuh = c; //配列cの先頭アドレスを設定。
phensuh = phensuh + 2; //phensuhを2データーサイズずらした位置に更新。
printf("%c\n", *phensuh); //'c'が表示される。

pointer = h; //配列hの先頭アドレスを設定。
pointer = pointer + 1; //pointerを1データーサイズずらした位置に更新。
printf("%d\n", *pointer); //15が表示される。



 int型の変数のメモリー番地を取得するには、int型の変数の前に&(=アンパサンド)を置けばいい。
 char型の変数のメモリー番地を取得するには、char型の変数の前に&を置けばいい。

int a = 1;
int *pointer;

pointer = &a; //int型の変数aのメモリー番地が代入される。
printf("%d\n",*pointer); //1が表示される。つまり、りpointerに格納されている値を番地として、その番地からに格納されているint型のデーターを表示。
printf("%d\n", pointer); //int型の変数aのメモリー番地が表示される。

 同様に、
char c = 'Z';
char *phensuh;

phensuh = &c; //char型の変数cのメモリー番地が代入される。
printf("%c\n", *phensuh); //'Z'が表示される。つまり、りphensuhに格納されている値を番地として、その番地からに格納されているchar型のデーターを表示。
printf("%d\n", phensuh); //char型の変数cのメモリー番地が表示される。



 150000番地に、'c'を書き込むには、
char *phensuh;

phensuh = (char *)150000;
*phensuh = 'c';
とすればいい。
 次の位置、つまり、150001番地に、'a'を書き込むには、
phensuh = phensuh + 1;
phensuh = 'a'
とすればいい。

 150000番地に、12を書き込むには、
int *pointer;

phensuh = (int *)150000;
*phensuh = 12;
とすればいい。
 次の位置、つまり、150004番地に、15を書き込むには、
pointer = pointer + 1;
*pointer = 15;
とすればいい。
 注意すべきは、ポインター値を150000から150004にする際、ポインター変数には、4を加算するのではなく、1を加算するだけでいいということである。
 int型のデーターサイズが4バイトであるので、int型のポインター変数に1加算するだけで、値は4倍の4の加算になる。

 ちなみに、100バイトのデーター型のポインター変数なら、そのポインター変数に1加算するだけで、その中身の値は、100加算される。
 つまり、ポインター変数というのは、1加算すれば、そのデーター型の次の位置にアクセスできる値になり、2加算したのなら、そのデーター型の次の次の位置にアクセスできる値になる、ということである。



char chairetsu[3] = {'A','B','C'};
char *cptr;

cptr = chairetsu;
cptr++;
*cptr = 'Z';
printf("%c\n", *cptr); //'Z'が表示される。

 このコードでは、'Z'が表示される。
 以上のコードと同じ処理を実行するのは下記のコードである。

char chairetsu[3] = {'A','B','C'};
char *cptr;

cptr = chairetsu;
*(++cptr) = 'Z';
print("%c\n", *cptr); //'Z'が表示される。

 このコードと同じ処理を実行するつもりで、下記のコードを記述するとエラーが発生する。

char chairetsu[3] = {'A','B','C'};

*(++chairetsu) = 'Z'; //エラー。lvalue required as incremet operand.
print("%c\n", *chairetsu);

 ここで、chairetsuは、配列名であるから、配列の先頭のメモリー番地を表す定数である。
 定数は、変数ではないので、代入が行えない。
 したがって、定数であるchairetsuは、代入がなされる、代入演算子の左辺の値とはなれない。つまり、lvalueとはなれない。
 ++演算子の対象には、代入が行える変数であることが必要である。つまり、lvalueであるとこが必要である。
 なので、lvalue required as increment operand.というエラーメッセージが出力される。
 つまり、++演算子の対象として、lvalueが要求される、というエラーメッセージが出力される。
 ちなみに、lvalueは、左辺値、である。




 C言語のポインターの知識は、以上のようなものである。
 ポインターということばから発想される1点を指し示すもの、みたいな意味、意味のバリエーション、によって、頭の中に確保される、知識の受け皿の数・知識の引き出しの数、に囚われないことが必要である。
 つまり、学習を進めてゆくにつれて継起してくる知識に合わせて、逐次、知識の受け皿の数・知識の引き出しの数、を増やす。そして、必要十分なだけの知識を把握し尽くしてゆく。
 ポインターということばの語感からもたらされる、知識の受け皿のバリエーション・知識の引き出しのバリエーション、に囚われてしまわないことが大切である。語感から直感的にもたらされた、知識の受け皿のバリエーション・知識の引き出しにのバリエーション、がぴったり整合していない。そういうこともある。そういう時には、どんどん、知識の受け皿のバリエーション・知識の引き出しのバリエーション、を適切なものに加除してゆく必要がある。
 そして、必要十分なだけの知識を把握し尽すことが必要である。