LoginSignup
50
40

More than 5 years have passed since last update.

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

Posted at

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

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

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

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

準備編

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

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

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

main関数

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

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

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

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

int main()
{
  return 0;
}

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

文字列

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

#include <stdio.h>

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

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

えーと、どこがダメなんでしょうか?printf関数シグネチャ(=関数宣言)を確認してみます:

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

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

配列

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

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]E1E2番目(0 から数える。)の要素を指し示す。

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

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

実践編

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

お題

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

#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;
}

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

ポインタレス・スタイル

もう飽きました。 TVの料理番組よろしく、完成したものがこちらになります:

#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;
}

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

続かない。

謝辞

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

50
40
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
40