
はじめに
前回は、scanf を使ってキーボードから値を受け取る方法を学びました。
今回は、C言語の理解で重要になる「ポインタ」を扱います。
この回の目的は次の4点です。
- ポインタが何を表すかを理解する
&と*の基本的な意味を覚えるscanfとポインタの関係を整理する- 初学者が混乱しやすい点を先に押さえる
ポインタとは何か
ポインタは、変数そのものではなく、「変数が置かれている場所」を扱うための仕組みです。
C言語では、変数はメモリ上のどこかに保存されています。
その場所を指し示す値を、ポインタと呼びます。
この段階では、次のように考えると理解しやすくなります。
- 変数: 値を入れる箱
- ポインタ: その箱がどこにあるかを示す情報
なぜポインタが必要なのか
ポインタは、関数の中から変数の値を書き換えたいときや、配列や文字列を効率よく扱いたいときに使います。
また、前回使った scanf でも、実際にはポインタが使われています。
たとえば次のコードです。
scanf("%d", &age);この &age は、age の値そのものではなく、age が置かれている場所を scanf に渡しています。scanf はその場所に、読み取った値を書き込みます。
まずはアドレスを確認する
ソースコード
pointer_address.c という名前で保存します。
#include <stdio.h>
int main(void) {
int age = 20;
printf("%d\n", age);
printf("%p\n", (void *)&age);
return 0;
}実行手順
1. 作業ディレクトリに移動する
cd ~/Desktop2. コンパイルする
clang pointer_address.c -o pointer_address3. 実行する
./pointer_address実行結果の例:
20
0x16fdff2acアドレスの値は実行するたびに変わることがあります。
そのため、2行目は例と同じでなくても問題ありません。
コードの読み方
age
printf("%d\n", age);これは変数 age に入っている値を表示しています。
結果は 20 です。
&age
printf("%p\n", (void *)&age);&age は、変数 age のアドレスを表します。& は「その変数のアドレスを取り出す演算子」です。
%p
アドレスを表示するときは、printf で %p を使います。
また、%p には void * 型を渡すのが標準的な書き方なので、(void *)&age と書いています。
ポインタ変数を使う
アドレスは、そのまま使うだけでなく、変数に保存できます。
そのための変数がポインタ変数です。
サンプルコード
#include <stdio.h>
int main(void) {
int age = 20;
int *p = &age;
printf("%p\n", (void *)p);
return 0;
}int *p の意味
int *p = &age;これは、int 型の変数を指すポインタ p を宣言しています。
int *p:int型を指すポインタ&age:ageのアドレス
つまり p には、age が置かれている場所が入っています。
* を使って値を取り出す
ポインタが指している先の値を取り出すには、* を使います。
この操作をデリファレンスと呼びます。
サンプルコード
pointer_value.c という名前で保存します。
#include <stdio.h>
int main(void) {
int age = 20;
int *p = &age;
printf("%d\n", age);
printf("%d\n", *p);
return 0;
}実行結果:
20
20*p の意味
p には age のアドレスが入っています。*p と書くと、そのアドレスの先にある値を取り出せます。
この例では、p は age を指しているので、*p は age の値と同じ 20 になります。
ポインタ経由で値を書き換える
ポインタを使うと、指している先の値を変更できます。
#include <stdio.h>
int main(void) {
int age = 20;
int *p = &age;
*p = 30;
printf("%d\n", age);
return 0;
}実行結果:
30*p = 30; によって、p が指している先、つまり age の値が 30 に変わります。
scanf とポインタの関係
ここで前回の内容に戻ります。
int age;
scanf("%d", &age);scanf は、入力された値を変数に書き込む必要があります。
そのため、変数の値ではなく、変数の場所を渡します。
つまり、scanf が &age を必要とするのは、ポインタの考え方があるからです。
関数に渡して値を書き換える例
ポインタは、関数の中で呼び出し元の変数を変更したいときにも使います。
#include <stdio.h>
void set_value(int *p) {
*p = 100;
}
int main(void) {
int x = 10;
set_value(&x);
printf("%d\n", x);
return 0;
}実行結果:
100なぜ値が変わるのか
set_value(&x); では、x のアドレスを関数に渡しています。
関数の中では、そのアドレスを受け取った p に対して *p = 100; を実行しています。
そのため、main の中の x 自体が書き換わります。
初心者がつまずきやすい点
& と * は役割が違う
&x:xのアドレスを取り出す*p:pが指している先の値を使う
この2つは見た目が似ていますが、意味はまったく異なります。
int *p; の * と *p = 30; の * は文脈が違う
int *p;これは「p はポインタ変数である」という宣言です。
*p = 30;これは「p が指している先に 30 を代入する」という意味です。
同じ * でも、宣言と利用で役割が異なります。
初期化していないポインタは使わない
次のようなコードは危険です。
int *p;
*p = 10;この p はどこを指しているか決まっていません。
この状態で *p を使うと未定義動作になります。
ポインタは、必ず正しいアドレスを代入してから使います。
よくあるエラー
warning: format specifies type ‘void *’ but the argument has type ‘int *’
原因: %p にそのまま int * を渡しています。
対処: (void *) にキャストして渡します。
正しい例:
printf("%p\n", (void *)p);実行時エラーや異常終了
原因: 初期化していないポインタや、不正なアドレスを使っています。
対処: &変数名 で得た有効なアドレスだけを使うようにします。
scanf で & を付け忘れる
これはポインタの理解と直結しています。
誤った例:
scanf("%d", age);正しい例:
scanf("%d", &age);練習用コード
アドレスと値を表示する
#include <stdio.h>
int main(void) {
int number = 42;
int *p = &number;
printf("%d\n", number);
printf("%p\n", (void *)p);
printf("%d\n", *p);
return 0;
}ポインタ経由で値を変更する
#include <stdio.h>
int main(void) {
int number = 42;
int *p = &number;
*p = 99;
printf("%d\n", number);
return 0;
}実行結果:
99まとめ
今回のポイントは次のとおりです。
- ポインタは変数のアドレスを扱う仕組みである
&はアドレスを取り出す*はポインタが指す先の値を扱うscanfが&変数名を必要とする理由は、値を書き込む場所を渡すためである- 初期化していないポインタは使ってはいけない
この回では、C言語のポインタの基本を確認しました。
ポインタは難しく感じやすいですが、まずは「変数の場所を扱う仕組み」として理解すると整理しやすくなります。
次回予告
次は、配列とポインタの関係を学びます。この2つを理解すると、文字列の扱いや関数の使い方がぐっと分かりやすくなります。
配列とポインタは深く関係しているため、この2つをつなげて理解すると、C言語の見え方が大きく変わってきます。

