はじめに
C言語を勉強していた際にポインタでつまずいた人は多いかと思います。私もその一人です。
今回は自分の実体験をもとに記載しようと思います。
対象者
・新卒時に会社でプログラミング学習していたときの自分
・C言語でプログラミング学習している初学者もしくは初心者
定番のポインタの説明
世の中に出ている様々なC言語の書籍を読む感じだと、以下のコードを見かけると思います。
int main()
{
int hoge = 555;
int* pointer = &hoge; // pointer変数はhogeを参照
printf("%d\n", hoge); // 555と表示
printf("%d\n", *pointer); // 555と表示
getchar();
return 0;
}
このコードを例として取り上げられた時、真っ先に疑問に浮かぶことは、
「何でポインタ変数が必要なんだろう?どういうときに使用されるのか??」かなと思います。
続いてこのようなコードもみたことあるかと思います。
#include<stdio.h>
void NotSwap(int y, int z);
void Swap(int* y, int* z);
int main()
{
int a = 2, b = 4;
int c = 3, d = 5;
printf("変更前:a = %d, b = %d\n", a, b);
NotSwap(a, b);
printf("変更後:a = %d, b = %d\n", a, b);
printf("変更前:c = %d, d = %d\n", c, d);
Swap(&c, &d);
printf("変更後:c = %d, d = %d\n", c, d);
getchar();
return 0;
}
void NotSwap(int y, int z)
{
int tmp = y;
y = z;
z = tmp;
return;
}
void Swap(int* y, int* z)
{
int tmp = *y;
*y = *z;
*z = tmp;
return;
}
出力結果
変更前:a = 2, b = 4
変更後:a = 2, b = 4
変更前:c = 3, d = 5
変更後:c = 5, d = 3
定番のswap関数です。NotSwap関数はいわゆる値渡しだからメイン関数で宣言している変数a,bの値は変わらないけど、swap関数はポインタ渡しだからメイン関数で宣言している変数c,dの値が変わるというものです。
このサンプルコードを学習した時、「何か値渡しに加えてポインタ渡しというのがあるのか」と理解した人は多いと思います。でもこれは個人的に悪い例かなと思いますし、この例を学習したところでいずれは「ポインタって必要なの?」という疑問がわくと思います。
グローバル変数
ポインタの学習を一通りした後、もしグローバル変数というものに出会ったら非常に厄介です。
#include<stdio.h>
int a, b; // グローバル変数
void Swap();
int main()
{
a = 3;
b = 5;
printf("変更前:a = %d, b = %d\n", a, b);
Swap();
printf("変更後:a = %d, b = %d\n", a, b);
getchar();
return 0;
}
void Swap()
{
int tmp = a;
a = b;
b = tmp;
return;
}
このコードは変数a,bをグローバル変数として宣言しています。先ほど記載したコードと異なる部分といえば、Swap関数の引数を必要としないことです。非常に便利です。
またグローバル変数には別ファイル間でその値を共有できる機能ができます。いわゆる外部参照と呼ばれるものです。
main.c
#include<stdio.h>
#include "func.h"
int a, b;
void Swap();
int main()
{
a = 3;
b = 5;
printf("変更前:a = %d, b = %d\n", a, b);
Swap();
printf("変更後(Swap):a = %d, b = %d\n", a, b);
Change999();
printf("変更後(Change999):a = %d, b = %d\n", a, b);
getchar();
return 0;
}
void Swap()
{
int tmp = a;
a = b;
b = tmp;
return;
}
func.c
#include "func.h"
extern int a, b; // 外部参照
void Change999()
{
a = 999;
b = 999;
}
func.h
#pragma once
void Change999();
出力結果
変更前:a = 3, b = 5
変更後(Swap):a = 5, b = 3
変更後(Change999):a = 999, b = 999
main.cpp側でグローバル変数を宣言し、func.cppでその値を外部参照しています。
ここまで理解した時、何か違和感を感じる人がいれば感の鋭い方なのかなと思います。
違和感とは
世の中にでているC言語で開発されたソフトウェアで、グローバル変数を中心としたソースコードを書いているエンジニアはほぼいないと思います。いましたら申し訳ありません。
何故いないと断言できるという理由については、このようなグローバル変数を多用していると、ソースファイルが100を超えると運用していくのは非常に困難を極め、バグを生み出す元凶となります。
自分一人がそのソフトウェアを開発し続けるなら、グローバル変数についての扱いを注意することは可能かと思いますが、多くの現場ではソフトウェアを一人で開発/運用という場合は稀です。
ポインタを理解する上で、はじめに大切なことは「グローバル変数は扱いを間違えると危険な存在」ということかなと思います。(Goto文と同様、使用する際は識者に相談すると良いかもです。)
最後に
とは言っても、私もポインタの理解を曖昧なまま実業務で先輩社員が作成したソースコードを熟読してやっと「ポインタって便利だな」と理解した身となります。なので初学者や初心者が参考書片手にポインタの知識は得ても、利点を知るのは難しいと思います。
後、先ほどのサンプルコードを読んでも理解が難しいと感じる人がいれば、C言語の絵本を読んでみるとイメージつきやすいかなと思います。
また昨今はChatGPTみたいな非常に便利なツールがあるので、それに頼ってみるのもいいかもしれません。