第12回|ポインタの基本を学ぶ:変数の場所を扱うしくみを理解する

当サイトでは、コンテンツの一部に広告を掲載しています。
第12回|ポインタの基本を学ぶ:変数の場所を扱うしくみを理解する
目次

はじめに

前回は、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 ~/Desktop

2. コンパイルする

clang pointer_address.c -o pointer_address

3. 実行する

./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 と書くと、そのアドレスの先にある値を取り出せます。

この例では、page を指しているので、*page の値と同じ 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言語の見え方が大きく変わってきます。

さらに学びたいあなたへ

用途ごとに選ぶ Linux のおすすめ本

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

のいのアバター のい UNIX Cafe マスター

Macintosh Color Classicから始まった旅は、長いWindows時代を経て、Windows10のサポート終了をきっかけにUNIXの世界へ戻ってきました。UNIX Cafeでは、UNIX・Linux・そしてMacな世界を、むずかしい言葉を使わず、物語のように書いています。プログラミングは、アイデアをコンピューターに伝えるための言葉です。簡単な単語と文法を覚えれば、誰でもコマンドを使えます。ぜひ一度、やさしいプログラミングの世界をのぞいてみてください。

Created by UNIX Cafe

目次