ポインタレス(Pointer-less)・プログラミング

プログラミング言語Cの特徴ってなんでしょうか？この質問に対して「ポインタ(Pointer)」を挙げる人はそれなりに居るでしょう。…たぶん居るよね。居ることにします。

他のプログラミング言語にもポインタに相当する概念（「参照(Reference)」と呼ばれることが多い）はありますが、とりわけC言語のポインタは難しいと言われること多いようです。時にはこんなコーディング規約を持ち出して、ポインタ使用が禁止されることもあるそうです。

> （俺がコードを理解できないから）ポインタの使用は禁止する。

なんだか心の声が漏れ聞こえる気もしますが、そこは大人な対応で迎えましょう。ええまあ、この記事は大人げないんですけど。ルールがポインタを使うなと言うなら、私だってちゃんと従いますよ。`*`とか`&`とか危なくて使えませんよね。


# 準備編
ここから本題。この記事では、プログラミング言語Cでポインタを**一切利用しない**「ポインタレス(Pointer-less)・プログラミング」にむけた説明を行います。

といっても、ポインタはC言語のさまざまな場面にあらわれる基礎概念です。ポインタを全面禁止するという制約により、一体どれだけの影響が出るのでしょう。

ポインタレス・プログラミングを実践する前に、C言語プログラム中でポインタがいつ利用されるのか、順番におさらいしておきましょう。


## main関数
では、さっそく`main`関数から書き始めましょう：

```c
int main(int argc, char *argv[])
```

> 「こんにちは、ポインタ警察です。ちょっとそこの`argv`を見せてください。その`*`は何ですか？」

はい。ごめんなさい。という訳で、`main`関数は引数なしバージョンしか使えません：

```c
int main()
{
  return 0;
}
```

コマンドライン引数(`argv`)の利用は諦めた方が良さそうです。

## 文字列
気を取り直して、懐かしのHello Worldでも書いて心を落ち着かせましょう。文字列(String)を出力するだけですね：

```c
#include <stdio.h>

int main()
{
  printf("Hello, World!\n");
  return 0;
}
```

> 「こんにちは、ポインタ警察です。(以下略)」

えーと、どこがダメなんでしょうか？`printf`関数シグネチャ（＝関数宣言）を確認してみます：

```c
int printf(const char * format, ...);
```

アッ、ハイ。C言語には「文字列型」というものは存在せず、文字列は文字型(`char`)の連なりにすぎません。文字列を扱うプログラムではポインタ利用は不可避です。文字列処理は諦めてください。


## 配列
うーん。文字列処理がダメなら、計算でもしますか。まずは配列(Array)を用意して…

```c
int main()
{
  int a[] = { 1, 2, 3, 4, 5 };
  int i, sum = 0;

  for (i = 0; i < 5; i++) {
    sum += a[i];
```

> 「こんにちは(以下略)」

ちょ、ちょっと待ってください。どこにもポインタなんて使ってませんよ。さすがに、このコードは許されるんじゃ？

問題の箇所は、配列要素にアクセスしている`a[i]`です。プログラミング言語Cの"仕様"を記述する、日本工業規格JIS X 3010:2003にはこうあります：
> 6.5.2.1 配列の添字付け
> 添字演算子`[]`は，`E1[E2]`が`(*((E1)+(E2)))`と等価であると定義する。2項`+`演算子に適用する型変換の規則によって，`E1`が配列オブジェクト（又は，配列オブジェクトの先頭要素へのポインタ）であり，`E2`が整数である場合，`E1[E2]`は`E1`の`E2`番目（0 から数える。）の要素を指し示す。

お堅い表現で読みにくいですが、「配列要素へのアクセス`E1[E2]`はポインタ演算`*(E1+E2)`と同等」と言っています。つまりコード上で`a[i]`と書いた部分は、コンパイラによって`*(a+i)`と読み替えられるのです。

さて、配列アクセスもアウトになりました。ポインタレス・プログラミングの道は遠く険しい。あとポインタ警察怖い。


# 実践編
とまあ、ここまでの内容で本旨は果たしてしまったのですが、一応実践編もやっときます。

## お題
ポインタレス・プログラムの題材として、非常に高度なプログラムを用意しました。標準入力(stdin)から2つの数値をよみとり、それら2つの値の足し算を行い、結果を標準出力(stdout)へ出力するプログラムです！

```c
#include <stdio.h>

int main()
{
  int a[2], sum;

  scanf("%d %d", &a[0], &a[1]);
  sum = a[0] + a[1];
  printf("%d\n", sum);
  return 0;
}
```

ポインタレス・スタイルでは、当然`scanf`も`printf`も文字列も配列も使えません。この辺りをどう対処するかが重要そうですね。


## ポインタレス・スタイル
<del>もう飽きました。</del> TVの料理番組よろしく、完成したものがこちらになります：

```c
#include <stdio.h>
#include <ctype.h>

int read_int()
{
  int value = 0;
  int ch;

  for (;;) {
    ch = getchar();
    if (ch == EOF || !isspace(ch))
      break;
  }
  while (ch != EOF && isdigit(ch)) {
    value = value * 10 + (ch - '0');
    ch = getchar();
  }
  return value;
}

void print_int(int value)
{
  int num;
  int digit;

  for (num = 1; num <= value; num *= 10)
    ;
  num /= 10;

  while (0 < num) {
    digit = value / num;
    putchar('0' + digit);
    value = value % num;
    num = num / 10;
  }
}

int main()
{
  int a1, a2, sum;

  a1 = read_int();
  a2 = read_int();
  sum = a1 + a2;
  print_int(sum);
  putchar('\n');
  return 0;
}
```

> オレはようやくのぼりはじめたばかりだからな
> このはてしなく遠いポインタレス坂をよ…

続かない。


# 謝辞
こちらの下らない記事は、下記ツイートよりアイデアを頂きました。この場を借りて御礼申し上げます。

- https://twitter.com/aokomoriuta/status/667178319507394560
- https://twitter.com/aokomoriuta/status/667178447106510848
- https://twitter.com/yohhoy/status/667186157482983424