【C言語の最重要機能】ポインタの基本を徹底解説!初心者でも必ずわかる

C言語を学ぶ上で、誰もが一度は「難しい」と感じる概念、それが**「ポインタ」**です。しかし、ポインタを理解することで、C言語の真の力を引き出し、より高度で効率的なプログラミングが可能になります。

この記事では、そんなポインタの基本的な考え方から、配列や文字列との関係まで、図や具体的なコードを交えながら、初心者の方にも分かりやすく解説します。


目次

ポインタとは? 〜 メモリアドレスを格納する変数

コンピュータのメモリは、データを保管するための広大なロッカーのようなものです。そして、各ロッカーには「0x1000」のようなユニークな**住所(アドレス)**が割り振られています。

私たちが int value = 10; のように変数を宣言すると、コンピュータは空いているロッカーの一つを確保し、そこに10という値を格納します。

ポインタとは、この**「変数が格納されているメモリアドレス」**そのものを値として保持するための、特殊な変数です。


ポインタの基本的な使い方

ポインタを扱うには、2つの重要な演算子を覚える必要があります。

  • & (アドレス演算子): 変数の前に付けると、その変数のメモリアドレスを取得します。
  • * (間接参照演算子): ポインタ変数の前に付けると、そのポインタが指し示しているアドレスに格納されているを取得します。

サンプルコードで理解する

言葉だけでは難しいので、実際のコードを見ていきましょう。

#include <stdio.h>

int main(void) {
    int value = 100; // 普通の整数型変数
    int *ptr;        // int型の値を指すためのポインタ変数

    // 1. value変数のアドレスを、ptrに代入する
    ptr = &value;

    printf("--- 変数 value の情報 ---\n");
    printf("値: %d\n", value);
    printf("アドレス: %p\n", (void *)&value); // %pはアドレスを表示する書式指定子

    printf("\n--- ポインタ ptr の情報 ---\n");
    printf("値 (valueのアドレス): %p\n", (void *)ptr);
    printf("ptr自身のアドレス: %p\n", (void *)&ptr);
    printf("ptrが指す先の値 (*ptr): %d\n", *ptr);
    
    // ポインタ経由で元の変数の値を変更する
    *ptr = 250;
    printf("\n*ptr を使って値を変更した後の value の値: %d\n", value);

    return 0;
}

実行結果(アドレスは環境により異なります):

--- 変数 value の情報 ---
値: 100
アドレス: 0x7ffc1e3a4b54

--- ポインタ ptr の情報 ---
値 (valueのアドレス): 0x7ffc1e3a4b54
ptr自身のアドレス: 0x7ffc1e3a4b58
ptrが指す先の値 (*ptr): 100

*ptr を使って値を変更した後の value の値: 250

この結果から、ptrvalue のアドレスを保持していること、そして *ptr を使って value の値を読み書きできることが分かります。

NULLポインタ

どこも指し示していない、無効なポインタを明示するために NULL という特別な値があります。ポインタ変数は、まだ指し示す先が決まっていない場合、NULLで初期化しておくのが安全な作法です。 int *safe_ptr = NULL;


配列とポインタの密接な関係

C言語では、配列とポインタは非常に密接な関係にあります。実は、配列名はその配列の先頭要素を指すポインタとして扱うことができます。

つまり、data という配列があるとき、data&data[0] と同じ意味になります。

この性質を利用すると、配列の各要素には2通りの方法でアクセスできます。

  1. 配列形式: data[i]
  2. ポインタ形式: *(data + i) (dataのアドレスからi個分進んだ先、という意味)

サンプルコード:

#include <stdio.h>

int main(void) {
    int scores[] = {85, 92, 78};

    printf("--- 配列 scores の各要素へのアクセス ---\n");
    for (int i = 0; i < 3; i++) {
        printf("要素[%d]:\n", i);
        // 同じ値とアドレスが表示されることを確認
        printf("  配列形式の値: %d, アドレス: %p\n", scores[i], (void *)&scores[i]);
        printf("  ポインタ形式の値: %d, アドレス: %p\n", *(scores + i), (void *)(scores + i));
    }

    return 0;
}

実行結果:

--- 配列 scores の各要素へのアクセス ---
要素[0]:
  配列形式の値: 85, アドレス: 0x7ffc...b44
  ポインタ形式の値: 85, アドレス: 0x7ffc...b44
要素[1]:
  配列形式の値: 92, アドレス: 0x7ffc...b48
  ポインタ形式の値: 92, アドレス: 0x7ffc...b48
要素[2]:
  配列形式の値: 78, アドレス: 0x7ffc...b4c
  ポインタ形式の値: 78, アドレス: 0x7ffc...b4c

結果が一致することからも、配列とポインタが内部的に深く結びついていることがわかります。


文字列とポインタ

C言語では、char * 型のポインタを使って文字列を扱うことが非常によくあります。

#include <stdio.h>

int main(void) {
    char *message = "Hello, Pointer!";
    
    printf("%s\n", message);
    
    return 0;
}

このコードは、メモリ上に確保された "Hello, Pointer!" という文字列データの先頭アドレスを、ポインタ変数 message が指し示している状態です。printf%s は、指定されたアドレスからNULL文字(文字列の終端)までを文字として表示するため、このようにして文字列全体を出力できます。

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

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次