ポインタ演算

ポインタは足し算できる

前回、ポインタが持っているのは番地だと確認しました。番地は数値なので、足し算ができます。ただし C の足し算は、素直な番地の足し算ではありません。ここが最初の山です。

int scores[3] = {80, 90, 70}; int *p = scores; /* 先頭の 80 を指す */ printf("%d\n", *(p + 1)); /* 90 */

p + 1 は「1 バイト先」ではなく「int 1 個分だけ先」を指します。int が 4 バイトなら、番地としては 4 進んでいます。double * なら 8 進み、char * なら 1 進みます。

なぜ型を書かされるのか

ポインタの宣言で型を書かされる理由が、ここで効いてきます。番地はロッカーの先頭番号でしかなく、そこから何バイトを 1 個分と数えるかは番地だけでは決まりません。その「1 個分の大きさ」を持っているのが型です。だから int *double * を混ぜられませんし、混ぜると進む幅が狂います。

実際に何バイト進んだかは、いったん char * に直すと確かめられます。char は 1 バイトなので、char * の世界では番地の差がそのままバイト数になります。

printf("%ld\n", (long)((char *)(p + 1) - (char *)p)); /* 4 */

p++ で 1 つ進む

p + 1 は新しい番地を作るだけで、p 自身は動きません。p 自身を動かすなら p++ です。

p++; /* p が次の要素を指すようになる */ printf("%d\n", *p); /* 90 */

*p++ のような書き方も文法としては通りますが、読みにくいので、慣れるまでは行を分けて書くほうが安全です。

ポインタどうしの引き算

同じ配列の中を指す 2 つのポインタは、引き算ができます。答えはバイト数ではなく 要素の個数 です。

int *q = scores + 2; printf("%ld\n", (long)(q - p)); /* 2 */

型は ptrdiff_t という専用の型なので、printf に渡すときは (long) にキャストして %ld で出すのが手軽です。この引き算は、あとで「見つけた要素が何番目か」を求めるときにそのまま使えます。

なお、ポインタどうしの足し算には意味がないので、C では書けません。番地と番地を足しても、どこも指さない数値になるだけだからです。

進めてよい範囲

進めてよいのは、配列の中と、最後の要素の 1 つ後ろまでです。1 つ後ろは「終端の目印」として指すことだけが許されていて、中身を読んではいけません。それより先に進めた時点で、動くかどうかは運任せになります。境界の見張りは、C ではコンパイラも実行環境もやってくれません。書いた人の責任です。

では、配列の上でポインタを動かして、進み方を数字で確かめてみましょう。

要件

  1. 点数は配列 scores に 4 つ読み込む
  2. 1 人目、2 人目、4 人目の点数を p、p+1、p+3 の間接参照で表示する
  3. 1 つ進んだときのバイト数を (char *) に直した差で求めて表示する
  4. p+3 と p の差を要素数として表示する
  5. 出力は「1人目は80点です」「2人目は90点です」「4人目は70点です」「1つ進むと4バイト先です」「p+3とpの差は3要素です」の形にする

入出力例

main("80 90 75 70") → "1人目は80点です 2人目は90点です 4人目は70点です 1つ進むと4バイト先です p+3とpの差は3要素です" main("100", "62", "88", "54") → "1人目は100点です 2人目は62点です 4人目は54点です 1つ進むと4バイト先です p+3とpの差は3要素です" main("45 45 45 91") → "1人目は45点です 2人目は45点です 4人目は91点です 1つ進むと4バイト先です p+3とpの差は3要素です"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.c
学習モード

メモ

ポインタ演算

⌘S で保存