C言語のポインタでつまずく3つの理由と理解するためのコツ

この記事の要点

  • ポインタでつまずく初心者が多いのは、C言語の文法がわかりにくいから
  • ポインタは、C言語で必要不可欠な重要テクニック
  • ポインタを習得するには、とにかく使ってみることが近道

C言語を使った副業やフリーランスととして独立するのが難しい理由の一つにC言語自体がPythonやPHPに比べると難しいということがあります。

C言語での副業はなぜ難しい?業務の種類と必要なスキルからわかるその理由

そして、C言語のポインタは、多くの初心者がつまずくところです。

この記事では、初心者がポインタでつまずきがちな理由と、すっきり理解するためのコツを解説します。

ポインタが難しいと感じている方や、ポインタの使い方やメリットがわからないという方に向けて、ポインタを効果的に習得する方法をご説明します。

C言語のポインタでつまずきがちな理由

多くの人がC言語のポインタでつまずいてしまう理由は、非常にシンプルです。
難しいことやっているからでもなく、学習者の理解力が足りないからでもありません。

一言で言うと、C言語の文法が複雑でわかりにくいからです。

C言語は、プログラミング言語が今よりもずっと少ない時代に発明され、長い年月を経て改良されてきました。
しかし文法の一部は、わかりにくいまま残ってしまっています。ポインタがその例です。

以下では、ポインタで初心者がつまずきがちな理由と、理解するためのコツについて解説します。

そもそもポインタとは?

C言語のポインタは、メモリ上の位置情報(アドレス)を取り扱うための概念です。

例えば、

int num = 10;

のように変数を宣言すると、そのデータ型(今回でいうとint)を扱うためのメモリが確保されます。

このとき確保されたメモリの位置情報がアドレスです。
そして、アドレスを取り扱うための変数がポインタ変数です。

ポインタを利用するためには、まず次の3点を覚えましょう。

  1. ポインタ変数を宣言する際は、変数名の頭に*をつける
  2. 変数のアドレスを取得する際は、変数名の頭に&をつける
  3. ポインタ変数から、ポインタが指しているデータを取り出す際は、変数の頭に*をつける

実際のコード例を見てみましょう。

int num = 10;
int *pNum; // ①ポインタ変数pNumを宣言
pNum = # // ②ポインタ変数pNumに、変数numのアドレス(&numで取得)を代入する
sprintf(“ポインタが指すデータは、%dです。”, *pNum); // ③*pNumでpNumが指しているデータを取り出す

コードを図で表すと以下のようなイメージです。
ポインタとは初心者がポインタでつまずく3つの理由と理解するためのコツ_ポインタとは

コードの実行結果は、「ポインタが指すデータは、10です。」となります。

ざっくり説明すると、以上がポインタの機能なのですが、なぜ多くの初心者がポインタでつまずいてしまうのでしょうか。

初心者がポインタでつまずく3つの理由について解説します。

配列との関係がわからない

C言語では、配列とポインタの取り扱いは似ている部分があり、それが混乱を招いています。

例えば、配列はポインタ変数で取り扱うことができます。

次のコードを例に考えてみます。

int array[] = {10 , 20, 30};
int *pArray = array; // arrayは式の中では、array[0]へのポインタに読み替えられる
printf(“%d”, array[1]); // 実行結果は「20」
printf(“%d”, pArray[1]); // 実行結果は「20」

コードを図で表すと以下のようなイメージです。

ポインタとは初心者がポインタでつまずく3つの理由と理解するためのコツ_配列

なぜ配列をポインタ変数で扱えるかというと、配列は、式の中では先頭要素へのポインタに読み替えられるからです。

ここが、初心者を困らせる配列とポインタのやっかいな関係性です。

ポインタ演算がわからない

ポインタ演算とは、ポインタ変数に対する加算や減算のことです。

次のコードを例に見ていきましょう。

int array[] = {10 , 20, 30};
int *pArray = array;
pArray++; // ポインタ演算
printf(“%d”, array[1]); // 実行結果は「20」
printf(“%d”, *pArray); // 実行結果は「20」

pArrayに1加算する、というポインタ演算を行っています。

このとき、例えばpArrayが100番地を指していたとします。そして

pArray++;

を行うと、pArrayは1つ進んで101番地を指すようになる...わけではありません。

ポインタにnを加算すると、そのポインタが指す型のサイズ × n進みます。

今回で言うと、intのサイズ(多くの処理系では4バイト) × 1 = 4進むことになります。

つまり、元が100番地なら104番地を指すことになります。

その結果、pArrayは配列の2番目の要素(=array[1])を指すようになりました。

このややこしさがわかりにくい原因の1つです。

何の役に立つかわからない

多くの初心者にとって、ポインタのメリットはイメージしにくいものです。

ポインタは、ある程度自分で使ってみないことには、どういうときに使うかわかりません。

そして、どう使えばいいかわからないから、なかなか使おうとしない、という無限ループに陥り、どんどんポインタから離れていってしまいかねません。

C言語のポインタが重要な理由

さて、ここまでポインタの不可解さについて説明してきました。

では、こんな厄介なものは使わずにC言語でプログラミングすることはできるでしょうか。

できることなら、アドレスなどは意識せず、直接データだけを操作したいところです。

しかし、答えはNOです。

むしろ、ポインタを使わずにプログラミングするほうが、難しくて面倒です。

ポインタは、使いこなせれば非常に強力なツールになります。

例えば、以下のような場面で大活躍します。

関数から複数の値を返してもらいたいとき

関数の戻り値は常に1つだけですが、引数はいくらでも追加できます。

そこで、引数にポインタを渡すことで、実質的に複数の値を取得することができます。

「処理結果を置く場所(=ポインタ)」を関数に渡して、関数の中では、ポインタが指すメモリにデータを格納する、というイメージです。

関数に配列や構造体を渡すとき

配列や構造体をそのまま関数に引数で渡そうとすると、コピーが発生します。

もし要素数10000の配列を渡したら、10000要素分そっくり全て別のメモリにコピーします。

するとメモリが余計に使われ、時間もかかります。

そこで、配列や構造体そのものではなく、ポインタを渡すことによって、無駄なコピーを避けることができます。

データそのものを渡さなくても、場所だけ教えてあげれば関数側で利用できます。

動的に確保したメモリを使うとき

配列は一度宣言してしまうと後から大きさを変更できません。

自由に配列の大きさを変えるためには、malloc()などを使って動的にメモリを確保します。

malloc()は、指定された大きさのメモリを確保して、そのメモリへのポインタを返す関数です。

つまり、確保されたメモリを利用するには、ポインタを使う必要があるのです。

このように、ポインタは多くの場面で使われるため、しっかり身に着けておくことが重要です。

C言語のポインタを理解するためのコツ

ポインタでのつまずきを解消するために、C言語のポインタを理解するためのコツをお伝えします。

配列とポインタは全くの別物であると覚える

確かに、ポインタ変数では配列を扱うことができます。しかし、あくまでも配列とポインタは別物です。
図にすると明らかです。

ポインタとは初心者がポインタでつまずく3つの理由と理解するためのコツ_メモリ上のどこか

以下の2点をしっかり理解して、混乱しないようにしましょう。

  1. ポインタは、アドレスを扱うためのもの
  2. 配列は、同じデータ型がいくつか並んだもの

ポインタで配列を扱えるというのは、たまたまそのとき指しているメモリが配列だったというだけのことです。
ポインタ=配列ではありません。

ポインタ演算は使わない

配列を扱う際にポインタ演算を使う必要はありません。
以下のコード例を見てみましょう。

int array[] = {10 , 20, 30};
int *pArray = array;
printf(“%d”, array[1]); // 実行結果は「20」
printf(“%d”, *(pArray+1)); // 実行結果は「20」
printf(“%d”, pArray[1]); // 実行結果は「20」

ご覧の通り、ポインタ変数に1を加算しても、pArray[1]のように指定しても、同じ結果が返ってきます。

実は、[]はポインタ演算を簡単に書くための仕組みです。

*(pArray+1)
pArray[1]

この2つは全く同じ意味です。
であるならば、より簡単に書けて、よりわかりやすい[]の方を使いましょう。

とにかく使ってみる

一般に、諸外国語と比較して日本語は難しいと言われます。しかし、私たちはごく当たり前に日本語を使いこなします。

これは、普段から使い慣れているからです。

同様に、C言語のポインタを理解する上では、ポインタを使い続けて経験を積むことが、最も効果的です。

とはいえ、ただ使ってみようとだけ言われても、何をしていいか困ってしまうでしょう。

そこで、まずは「C言語のポインタが重要な理由」で解説した3つのシーンを想定して、コードを書いてみることをおすすめします。

  • 関数から複数の値を返してもらいたいとき
  • 関数に配列や構造体を渡すとき
  • 動的に確保したメモリを使うとき

このような実装を何度も繰り返している内に、自然にポインタを使える状態になります。

まとめ

ポインタは、それ自体が難しいわけではありません。初心者がつまずくのは、C言語の独特でわかりにくい文法のためです。

そして、わかりにくい文法を身に着けるためには、習うより慣れろでとにかく使ってみるのが一番効果的です。

ここまで読んでいただいた方なら、ポインタの基礎知識は手に入っているはずですから、後は実践を重ねていけば、スムーズに使いこなせるようになります。

ポインタはC言語にとって必要不可欠な重要テクニックです。

使いこなせるようになると、できることがぐっと増えます。

ぜひポインタを身に着けて、エンジニアとしてレベルアップしていきましょう!