はじめに
- これまでの学習に続いて、今回はポインタ関連で学んだ結果をまとめてみました。
※今回の内容は、スッキリわかるC言語入門 第2版のp.363~p.377となります。
学習環境
- 今回はpaiza.ioのC言語のエディタを使いました。
3つのからくり構文
その1:関数宣言の解釈
関数の引数や戻り値に配列型を指定すると、ポインタ型を指定したものと見なされる。
Main.c
#include <stdio.h>
void funcA(int* x) {
printf("2番目の要素は%dです。\n", x[1]);
}
int main(void){
int array[3] = {2, 4, 6};
funcA(array);
return 0;
}
- 上記のサンプルコードの場合、
funcA
関数の引数宣言は以下のものと実質的に同義だそうです。funcA(int x[3])
funcA(int x[])
funcA(int*)
その2:配列変数の評価
ソースコード上に書かれた配列変数名は、その配列の先頭要素の位置を示すアドレスに「化ける」。
- 上記の
funcA
関数に配列を渡す場合、これまではfuncA(array);
と書いてきましたが、funcA(&array[0]);
と書いても同じ意味になるそうです。
その3:添え字演算子の評価
ポインタ変数pがある時、p[0]
と*p
は同じ意味になる。
またpがvoid*型以外である時、p[整数]
と*(p+整数)
も同じ意味になる。
- このルールに従うと、上記のサンプルコードの
funcA
関数は以下のように書き換えられます。
Main.c
void funcA(int x[]) {
printf("2番目の要素は%dです。\n", *(x+1));
}
ポインタ演算
** X*
型のポインタ変数に加算や減算を行うと、その計算結果のアドレス値は「X*
型のサイズ」を単位として増加・減少する。**
- 例えばint型のポインタ変数
a
に対して1加算すると、int型は4byteなので、その結果のアドレス値は1ではなく4増えます。- 同様にshort型のポインタ変数
b
に対して3加算すると、short型は2byteなので、その結果のアドレス値は3ではなく6増えることになります。
- 同様にshort型のポインタ変数
- この「ポインタ演算」が存在することで、上記の「3つのからくり構文」の「その3:添え字演算子の評価」のような書き方が出来るそうです。
オーバーラン
- 以下のように要素数3の配列
array
に対して、大きすぎる(あるいは小さすぎる)添え字を指定することで、配列array
のために確保されている領域外へのメモリアクセスをすることが出来ます。 - このように本来割り当てられていないメモリ領域へのアクセスを
オーバーラン
と呼びます。 - 意図せずにオーバーランする不具合を抱えたプログラムは、サイバー犯罪に悪用されることもあります。
Main.c
int main(void){
int array[3] = {2, 4, 6};
// 配列のために確保されている領域外へのメモリアクセス
printf(array[-1]);
printf(array[3]);
return 0;
}