C言語のポインタの基本を探る

C言語のポインタの挙動をサンプルコードを通じて考えます

C言語、学びはじめました

とあるイベントに参加したことをきっかけにC言語を勉強しています。 自分自身も手探りで学んでいる最中ですが、C言語を勉強する上で学習コストの高いポインタについて、今の状態でわかっていることをまとめておきます。

間違いやより適切な表現等のご連絡、ご相談はこちらのリンクよりお願いいたします。

ポインタとは

ポインタは、メモリ上のアドレス(場所)を格納する変数です。通常の変数が「値」を持つのに対し、ポインタ変数は「値が格納されている場所」を持ちます。

例えるなら:

  • 通常の変数 = 家の中の物
  • ポインタ = 家の住所

住所がわかれば、その家に行って物を取り出したり、置いたりできる、という感じで理解しています。

ポインタの長所:関数を超えて変数を操作できる

C言語では、通常、関数に変数を渡すと「値のコピー」が渡されます。そのため、関数内で値を変更しても、呼び出し元の変数には影響しません。

しかし、ポインタを使えば、関数を超えて元の変数を直接操作できます。これがポインタの使いどころになってきます。

サンプルコード

では、実際のコードで確認してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>

// main関数からポインタでnbが指す変数のアドレスを受け取っている
void single_pointer(int *nb)
{
	*nb = 42; // ポインタが指す先(=nの値)に42を代入
}

int main(void)
{
	int *nb; // ポインタ変数
	int n;   // 通常の変数

	n = 32;     // nを32で初期化
	nb = &n;    // ポインタ変数nbに変数nのアドレスを格納

	printf("before : %d\n", *nb); // single_pointer関数を呼ぶ前のnの値を確認
	printf("n address: %p\n", (void *)&n); // 変数nのアドレス
	printf("nb value: %p\n", (void *)nb);  // ポインタ変数nbの値(=変数nのアドレス)

	single_pointer(nb); // 関数にnbの値(=変数nのアドレス)を渡す
	printf("after : %d\n", *nb); // single_pointer関数実行後のnの値を確認

	return (0);
}

実行結果(例)

実行環境によってn adressとnb valueは変動すると思いますが、値は同一になっているはずです。

ちなみに自分の実行環境はOS: Ubuntu 24.04.3 LTS x86_64、コンパイラはcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0、cc -Wall -Wextra -Werrorオプションでコンパイルしています。

1
2
3
4
before : 32
n address: 0x7ffeeb8b9a3c
nb value: 0x7ffeeb8b9a3c
after : 42

コードの詳細解説

1. 各種変数の宣言と初期化

main関数の中から見ていきます。

1
2
3
4
int n;      // 通常の変数
int *nb;    // ポインタ変数(int型を指すポインタ)
n = 32;     // nに値を代入
nb = &n;    // nbに変数nのアドレスを代入
  • int *nb* は「ポインタ型」を示します
  • &n は「変数nのアドレスを取得」する演算子です
  • これで nb は変数 n の場所を「知っている」状態になります

2. ポインタが指す先の値を確認

1
printf("before : %d\n", *nb);  // 出力: 32
  • *nb は「ポインタnbが指す先の値」を意味します(間接参照)
  • この時点では n の値である 32 が表示されます

3. アドレスの確認

1
2
printf("n address: %p\n", (void *)&n);  // 変数nのアドレス
printf("nb value: %p\n", (void *)nb);   // ポインタnbの値

両方とも同じアドレスが表示されます。これは nb が正しく n を指していることを示しています。

4. 関数を超えて変数を操作

1
single_pointer(nb);  // nのアドレスを渡す

ここでは、main関数の外に出てsingle_pointer 関数に nb(=変数nのアドレス)を渡しています。

single_pointer関数内では:

1
2
3
4
void single_pointer(int *nb)
{
	*nb = 42;  // ポインタが指す先に42を代入
}

*nb = 42 により、ポインタが指す先、つまり main関数内の変数nの値が直接変更されます

5. 結果の確認

1
printf("after : %d\n", *nb);  // 出力: 42

関数実行後、変数 n の値が 32 から 42 に変わっていることが確認できます。

メモリのイメージ図

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
---

アドレス: 0x7ffeeb8b9a3c  
変数名: n          
値: 32 → 42                ← ポインタnbが指している

---

アドレス: 0x7ffeeb8b9a40
変数名: nb
値: 0x7ffeeb8b9a3c (nのアドレス)

---

ポインタ関連の演算子

演算子名前意味
*間接参照演算子ポインタが指す先の値にアクセス*nb = 42
&アドレス演算子変数のアドレスを取得nb = &n

なぜポインタが必要なのか?

1. 関数から複数の値を返せる

通常、関数は return で1つの値しか返せませんが、ポインタを使えば複数の値を変更できます。

1
2
3
4
5
void calculate(int a, int b, int *sum, int *product)
{
	*sum = a + b;
	*product = a * b;
}

上記のコードだと、void型なので直接には何も値は返りませんが、sumとproductの値は変更されるので二種類の変数が「返って」いることになります。

2. 大きなデータを効率的に扱える

構造体や配列など大きなデータをコピーせず、アドレスだけを渡すことで処理が高速になります。

3. 動的にメモリを確保する際に使う

malloc などで動的にメモリを確保する際、ポインタが必要です。

まとめ

  • ポインタは変数のアドレス(場所)を格納する変数
  • & でアドレスを取得し、* で指す先の値にアクセス
  • 関数を超えて変数を直接操作できるのが最大の利点
  • メモリを効率的に扱うために不可欠

自分でも書いていて迷子になりかけていますが、だいたい「住所を教えて、その場所の物を操作する」というイメージで理解しています。実際にコードを動かして、アドレスと値の変化を確認してみてください!

おすすめサイト

ここまで読んでいただいて元も子もない気がしますが、個人的にC言語を勉強するにあたって一番おすすめのサイトがあります。

だえうホームページ

文量は長めですが、その分初心者でもわかりやすくポインタを理解できるサイトになっています。

最初の最初はこの記事から読むと良さそう→【C言語】ポインタを初心者向けに分かりやすく解説 | だえうホームページ

以上

最終更新 10月 09, 2025 11:05 +0900
発言は個人の見解であり、所属組織とは関係ありません。
Hugo で構築されています。
テーマ StackJimmy によって設計されています。