はじめに
こんにちは 高専一年生ロボコン部部員です
以前、ポインタ機能について勉強した際、メモリや変数の型の概念について知りました
かなり理解に苦しんだので、共有したいと思います
あくまで勉強して半年レベルの初心者の捉え方なので、間違いがあると教えていただけると嬉しいです
ポインタの意義
まず、ポインタ変数の必要性や存在意義について説明します
ポインタ変数の仕事
ポインタ変数とは、
アドレスを保存しアドレス元を読み書きすることができるものです
これは主に関数の引数として本領を発揮します
C言語の関数はPythonなどと違い、
戻り値として返すことのできる値は一つのみです
しかしアドレスを保存したポインタ変数を用意すれば
戻り値とは別に変数をその関数内で遠隔操作が可能なのです
ポインタ変数の仕組み
これからは、アドレスの正体やメモリの考え方を説明していきます
覚えなくても問題ないのですが知っておくとより興味を持つことができるので説明します
メモリとは
まず、コンピュータには
「0か1しか保存できないミクロサイズの機器」が並んでいます
その数はまさに膨大であるため無限にあると思っていただいて問題ありません
※実際は有限ですが
あれ?ではなぜ、C言語の変数にはもっと膨大な数値を入れることができるのでしょう?
それは、コンピュータはそれら電子機器を組み合わせ、2進数で様々な数を表しているからです
つまり、int型などは
電子機器をその変数に何個用いるかを決定するのです
アドレスとは
先ほど述べた通り変数ごとに使う電子機器が決まります
しかし
どこの電子機器が使われているのかはコンパイラの気まぐれであるため
プログラマには分からないし分かる必要もありません
しかし考えてみてください
ポインタ変数はアドレスを記憶して元の変数にアクセスします
アドレスは分かりません
だったら使いようがないのではないでしょうか
アドレスの求め方
なんと、変数の頭につけてアドレスをだす素晴らしい記号があります
& です
変数の頭に&をつけるとアドレスが出ます それをもってこのコードをご覧ください
#include <stdio.h>
int main(void)
{
int a;
printf("%d",&a);
return 0;
}
僕の環境では「1375980」と書かれました
ただしこの値はコンパイラや環境によって異なります
このアドレスを保存することではじめてポインタ変数が使えるのです
ポインタ変数の使い方
見本
#include <stdio.h>
int main(void)
{
int a = 0;
int *p;
p = &a;
printf("%d",a);
(*p) ++;
printf("%d",a);
return 0;
}
ポインタ変数の宣言
int *p;
宣言はこれで可能です
int型に必ずする必要はありません
ポインタ型とはあくまで型の +α のようなものなのですね
ポインタ変数のアドレスの保存
p = &a;
これで変数aのアドレスを保存しました
ここでは*をポインタ変数につけません
ポインタ変数を使ったアドレス元の変更
(*p) ++;
これで変数aの値を変更しました
ここでは*をポインタ変数につけます
関数にアドレスを引数として渡す際の注意点
#include <stdio.h>
void point(int *p);
int main(void)
{
int a = 0;
printf("%d",a);
point(&a);
printf("%d",a);
return 0;
}
void point(int *p){
(*p) ++;
}
point関数に変数aのアドレスを渡したところまではいいのですが
問題はそれを受け取るポインタ変数部分です
*がついているのにアドレスを保存できているのです
じつは宣言の時に使う*と、
保存元とアクセスするときにつかう*は別の役割を担うのです
つまり、偶然point関数の引数部分で宣言しているだけで
*がついていないことと同じなのです
ポインタ変数と配列
こちらは難しいお話です
まずは配列の仕組みについて考えます
配列とメモリの関係
配列は、複数の長めのメモリが使われます
そのなかで要素ごとに使うメモリが区切られているわけですが、
その間隔は同じです
なぜなら、
C言語において、配列の値は全て同じ型であるため、それぞれに使用するメモリの数は同じになるためです
□□□□|□□□□|□□□□|□□□□
変数 変数 変数 変数
→要素数4の配列
配列のアドレス
アドレスは要素数が増えても同じ間隔で増加します
仮にこんなコードがあったとします
int array[3] = {0};
この時、
&array[0]
&array[1]
&array[2]
はそれぞれ要素数0,1,2の位置のアドレスを表します
では、これは?
array
実はこれ、
&array[0]
と同義なのです
その配列の最初の要素のアドレスを表すのです
『ポインタ配列』のように扱うことができるポインタ変数
じつは、ポインタ変数は配列の先頭のアドレスを受け取るだけで
配列すべての要素にアクセスできるように扱うことのできるポインタ変数に変化します
なぜそれだけで先頭以外の要素にもアクセス可能になるのでしょう?
ポインタ変数は、配列の型から一個分の変数の使うメモリ数を予測し
先頭以外の要素のアドレスを計算して求めてくれるからです
つまりこのコードは成り立ちます
#include <stdio.h>
void array_change(int *array2);
int main(void){
int array[3] = {0};
printf("%d",array[2]);
array_change(array);
printf("%d",array[2]);
}
void array_change(int *array2){
array2[2] = 1;
}
→01
さいごに
ここまで読んでいただいてありがとうございました
参考にしていただけると嬉しいです