いきなりですが問題です.
ポインタ変数pとqが,ある文字列のどこかの位置を指し示しているものとする.このとき,その中間の位置(ポインタ値)をCの式として表しなさい.
で,答えです.
p + (q - p) / 2
pとqのどちらが大きくても,それなりの中間の位置になります.q-pが偶数なら,ちゃんと中間の位置になります.奇数なら,つまり例えば"ABCD"の先頭をp,末尾をqが指し示しているなら,上の式で2文字目を指し示すポインタ値になりますが,pとqが逆なら,3文字目になるでしょう.
この問題,「中間だから平均値」ということで,
(p + q) / 2
と書いたら間違いです.
というのも,アドレス演算をするとき,ポインタ値同士の和は許されていないからです.
正解の式では,以下の演算がなされます.どのステップも,正当な演算です.
- 「q - p」により,その差を表す整数値を求める.[ポインタ - ポインタ ⇒ int]
- 「(q - p) / 2」により,その差の半分を表す整数値を求める.[int / int ⇒ int]
- そして「p + (q - p) / 2」により,pとqの中間の位置を求める.[ポインタ + int ⇒ ポインタ]
値同士の和をしてはいけない,日常の例として,時刻があります.以下,「時刻」と「時間」の違いに注意して読んでいってください.
時間に比例してできる作業(数をこなす単純労働など)をすることにします.開始時刻を今日の15時,終了時刻を明日の4時半として,中間の時刻がいつか知りたいときに,15時と明日4時半をいきなり足し算するわけにはいきません.
「15時」と「28時30分」とした上で,足し算して2で割って,21時45分という答えを出すことは可能です.
ここで足し算ができる理由を,いくつか文字を置いて,説明します.
- 今日の0時の時点をT0.ここからの経過時間はt0 = 0
- 15時の時点をT1.T0からの経過時間をt1 = 15時間
- 翌朝4時半の時点をT2.T0からの経過時間をt2 = 28時間30分
そして,求めたいのは,T1とT2の中間の時刻です.しかしこれは,(T1 + T2) / 2ではなく,(t1 + t2) / 2 = t1 + (t2 - t1) / 2により21時間45分を求めて,これは今日の0時を基点としたら,今日の21時45分という時刻になる,ということです.
T0, T1, T2に時刻の情報,t0, t1, t2に時間(T0からの経過時間)の情報を持たせるなら,この中間の時刻は「T1 + (t2 - t1) / 2」となります.
ここで,経過時間同士の引き算をしていますが,「2つの時刻の差を求める」演算が許されるなら,「T1 + (T2 - T1) / 2」と書き換えられます.ということで,ポインタの演算とまったく同じ式で表現できることが確認できました.
時刻同士の和を求めることはできません.時刻の差を「時間」として求めたら,時間同士の加減算,定数倍は可能です.ある時刻と,経過時間(正でも,0でも負でもかまいません)を足すことができ,時刻が得られます.