6日でできる 新C言語入門|ポインタにつながる変数アドレスの基本

変数には値だけでなく、メモリ上の場所もある。
アドレスの基本を理解して、ポインタ学習への第一歩を踏み出そう。

C言語を学習していると、最初は変数を値を入れる箱のように考えることが多いです。たとえば、int型の変数に120を入れたり、double型の変数に2480.5を入れたりします。

しかし、C言語をさらに深く学ぶためには、変数がどこに保存されているのかも意識する必要があります。

コンピュータのメモリには、たくさんのデータが保存されています。変数の値もメモリ上のどこかに置かれています。このメモリ上の場所を示す情報が、アドレスです。

アドレスは、変数の値そのものではありません。変数がメモリ上のどこにあるのかを示す場所の情報です。

C言語では、変数のアドレスを取得できます。これが、ポインタを理解するための大切な入口になります。ポインタは、アドレスを保存するための変数です。そのため、まずは普通の変数にもアドレスがあることを理解しておくことが大切です。

この記事では、変数のアドレスを取得する方法、アドレスを表示するときの書き方、sizeof演算子で変数のサイズを調べる方法、void*へのキャストの意味、そしてポインタ学習へどのようにつながるのかを、サンプルプログラムを使ってやさしく解説していきます。

変数は値とアドレスを持っている

変数というと、値を保存するものというイメージが強いかもしれません。

たとえば、次のような変数があるとします。

int count = 120;

この変数countには、120という値が入っています。

ただし、コンピュータの中では、この120という値はメモリ上のどこかに保存されています。その保存場所を表すのがアドレスです。

つまり、変数には次の2つの見方があります。

見方内容
変数に保存されているデータ
アドレスその変数がメモリ上に置かれている場所

C言語では、値を取り出すだけでなく、アドレスを調べることもできます。

このアドレスを理解すると、ポインタ、配列、関数への値渡しとアドレス渡しなど、C言語らしい仕組みが見えてきます。

アドレスはメモリ上の場所を示す情報

メモリは、データを保存するための広い作業場所のようなものです。

その中には、変数の値、配列の要素、関数の実行に必要な情報などが置かれています。

変数ごとに保存場所があるため、変数ごとにアドレスもあります。

たとえば、countというint型の変数とpriceというdouble型の変数がある場合、それぞれ別の場所に保存されるため、基本的には別々のアドレスを持ちます。

変数メモリ上の見方
count120どこかのアドレスに保存されている
price2480.5countとは別の場所に保存されている
rate0.08rate用の場所に保存されている

アドレスは、通常16進数のような形式で表示されます。

ただし、実際に表示されるアドレスの値は、実行する環境やタイミングによって変わります。そのため、実行例と同じアドレスにならなくても問題ありません。

図:変数の値はメモリ上の場所に保存される

この図から分かること

この図から分かるのは、変数の値がメモリ上のどこかに保存されているということです。

countやpriceのような変数には、それぞれ値があります。同時に、その値が保存されている場所もあります。その場所を示す情報がアドレスです。アドレスを理解すると、ポインタが何を保存する変数なのかを考えやすくなります。

変数のアドレスを取得するには&演算子を使う

C言語では、変数名の前に&演算子を付けると、その変数のアドレスを取得できます。

&count

これは、countの値ではなく、countが保存されている場所を表します。

値とアドレスの違いを整理すると、次のようになります。

書き方意味
countcountに入っている値
&countcountが保存されているアドレス

この違いはとても大切です。

countと書けば、変数の中身を使います。
&countと書けば、変数の場所を使います。

ポインタを学ぶと、このアドレスをポインタ変数に代入して扱うようになります。

アドレスを表示するには%pを使う

printfでアドレスを表示する場合は、書式指定子%pを使います。

printf("%p\n", (void*)&count);

ここで使っている(void*)は、アドレスをvoid*型へ変換するための書き方です。

%pでアドレスを表示するときは、標準的にはvoid*型にキャストして渡します。

書き方意味
&countcountのアドレスを取得する
(void*)&countcountのアドレスをvoid*型として扱う
%pアドレスを表示するための書式指定子

実際の開発環境によっては、&countのまま表示できる場合もあります。しかし、標準的で安全な書き方としては、(void*)&countのようにvoid*へキャストしてから%pで表示します。

void*は型を限定しないアドレス用の型

void*は、特定の型に限定しないポインタ型です。

たとえば、int型の変数のアドレスはint型へのポインタ、double型の変数のアドレスはdouble型へのポインタとして扱われます。

アドレスの種類考え方
int型変数のアドレスint型のデータを指すアドレス
double型変数のアドレスdouble型のデータを指すアドレス
void*型を限定しない汎用的なアドレス

%pはアドレスを表示するための書式指定子です。このとき、void*型の値を渡すのが標準的な形です。

そのため、int型でもdouble型でもfloat型でも、アドレスを表示するときには(void*)を付けて型をそろえます。

printf("%p\n", (void*)&price);

この書き方に慣れておくと、ポインタを学ぶときにも理解しやすくなります。

変数の値・サイズ・アドレスを表示するプログラム

次のプログラムでは、複数の型の変数について、値、サイズ、アドレスを表示します。

プロジェクト/ファイル名: Lesson49_1/main.c

#include <stdio.h>

int main(void) {
    int count = 120;
    double price = 2480.5;
    float rate = 0.08f;
    long stock = 3500L;

    // int型の変数について、値、サイズ、アドレスを表示する
    printf("countの値: %d, サイズ: %zuバイト, アドレス: %p\n",
        count, sizeof(count), (void*)&count);

    // double型の変数について、値、サイズ、アドレスを表示する
    printf("priceの値: %.2f, サイズ: %zuバイト, アドレス: %p\n",
        price, sizeof(price), (void*)&price);

    // float型の変数について、値、サイズ、アドレスを表示する
    printf("rateの値: %.2f, サイズ: %zuバイト, アドレス: %p\n",
        rate, sizeof(rate), (void*)&rate);

    // long型の変数について、値、サイズ、アドレスを表示する
    printf("stockの値: %ld, サイズ: %zuバイト, アドレス: %p\n",
        stock, sizeof(stock), (void*)&stock);

    return 0;
}

実行例

countの値: 120, サイズ: 4バイト, アドレス: 000000E25F8FF7A4
priceの値: 2480.50, サイズ: 8バイト, アドレス: 000000E25F8FF7C8
rateの値: 0.08, サイズ: 4バイト, アドレス: 000000E25F8FF7E4
stockの値: 3500, サイズ: 4バイト, アドレス: 000000E25F8FF804

表示されるアドレスは、実行環境や実行するタイミングによって変わります。

そのため、上の実行例と同じアドレスが表示されなくても問題ありません。大切なのは、それぞれの変数に別々のアドレスがあることを確認することです。

sizeof演算子で変数のサイズを調べる

sizeof演算子を使うと、型や変数がメモリ上で何バイト使うのかを調べられます。

sizeof(count)

sizeofは、指定した型や変数のサイズをバイト単位で返します。

書き方調べられる内容
sizeof(int)int型のサイズ
sizeof(double)double型のサイズ
sizeof(count)count変数のサイズ
sizeof(price)price変数のサイズ

sizeofの結果をprintfで表示するときは、%zuを使います。

printf("%zu\n", sizeof(count));

sizeofの戻り値はsize_t型です。size_t型の値を表示するには、%zuを使うのが標準的な書き方です。

型によって必要なメモリサイズは異なる

変数の型が違うと、必要なメモリサイズも変わります。

たとえば、int型は4バイト、double型は8バイトになることが多いです。ただし、型のサイズは環境によって異なる場合があります。

サイズの目安内容
int4バイト程度整数を扱う
float4バイト程度単精度の実数を扱う
double8バイト程度倍精度の実数を扱う
long環境によって異なるより広い整数を扱う

同じC言語でも、コンパイラやOS、実行環境によってサイズが変わる可能性があります。

そのため、正確なサイズを知りたいときは、sizeof演算子で確認するのが確実です。

図:&演算子とsizeof演算子で場所と大きさを調べる

この図から分かること

この図から分かるのは、変数について調べられる情報が1つだけではないということです。

countと書けば変数の値を使います。&countと書けば変数のアドレスを取得できます。sizeof(count)と書けば、その変数が何バイトの領域を使っているかを調べられます。値、場所、大きさを分けて考えると、メモリの理解が進みます。

値・サイズ・アドレスを表で整理する

サンプルプログラムで扱った変数を、表で整理してみましょう。

変数サイズの目安アドレス取得
countint1204バイト&count
pricedouble2480.58バイト&price
ratefloat0.084バイト&rate
stocklong3500環境によって異なる&stock

ここで大切なのは、値とアドレスを混同しないことです。

countの値は120です。
&countはcountが保存されている場所です。

priceの値は2480.5です。
&priceはpriceが保存されている場所です。

この違いを理解できると、ポインタを学ぶ準備が整ってきます。

アドレスは実行するたびに変わることがある

アドレスの表示結果は、毎回同じになるとは限りません。

同じプログラムを実行しても、次のような理由でアドレスが変わることがあります。

理由内容
実行環境が違うOSやコンパイラによってメモリ配置が異なる
実行タイミングが違うプログラムを置くメモリ位置が変わることがある
最適化や設定が違うコンパイラの設定によって配置が変わることがある

そのため、アドレスの数字そのものを覚える必要はありません。

学習で大切なのは、変数ごとにメモリ上の場所があり、その場所を&演算子で取得できるという考え方です。

アドレスはポインタの入口になる

ポインタは、アドレスを保存するための変数です。

たとえば、int型の変数countのアドレスを保存したい場合、int型へのポインタを使います。

int count = 120;
int* pCount = &count;

ここで、pCountはcountのアドレスを保存しています。

書き方意味
countcountの値
&countcountのアドレス
pCountcountのアドレスを保存しているポインタ
*pCountpCountが指す場所にある値

ポインタを本格的に使うには、アドレスを理解している必要があります。

そのため、ポインタを学ぶ前に、変数のアドレスを表示して、変数がメモリ上に存在していることを確認しておくと理解しやすくなります。

アドレスを使うと関数で変数を直接変更できる

C言語では、通常、関数に変数を渡すと値のコピーが渡されます。

しかし、アドレスを渡すと、関数の中から呼び出し元の変数を変更できるようになります。

たとえば、scanfで整数を入力するときに、変数名の前に&を付けます。

scanf("%d", &count);

これは、scanfにcountのアドレスを渡しているためです。

scanfは、そのアドレスを使って、countのメモリ上の場所に入力値を書き込みます。

このように、アドレスは単に表示するだけでなく、変数を外部の処理から変更するためにも使われます。

図:アドレスを理解するとポインタとscanfが見えてくる

この図から分かること

この図から分かるのは、アドレスの理解がポインタやscanfの理解につながるということです。

&countで取得できるのは、countの値ではなくcountの場所です。ポインタはその場所を保存できます。また、scanfは変数のアドレスを受け取ることで、その場所へ入力値を書き込みます。つまり、アドレスはC言語の重要な仕組みを支える土台です。

アドレス表示で注意したいこと

アドレスを表示するときは、次の点に注意しましょう。

注意点内容
%pを使うアドレス表示には%pを使う
void*へキャストする(void*)&変数名の形で渡す
アドレス値は毎回変わることがある実行例と同じ数値にならなくても問題ない
値とアドレスを混同しない変数名は値、&変数名はアドレス
型のサイズは環境依存の場合があるsizeofで確認する

特に、値とアドレスの違いは重要です。

次の2つは意味が違います。

count
&count

countは変数の値です。
&countは変数のアドレスです。

この違いをしっかり押さえると、ポインタの学習に入りやすくなります。

変数アドレスを理解するとC言語の世界が広がる

変数のアドレスは、メモリ上で変数が保存されている場所を表す情報です。

C言語では、&演算子を使って変数のアドレスを取得できます。取得したアドレスは、printfの%pを使って表示できます。そのとき、(void*)&countのようにvoid*へキャストして渡すのが標準的な書き方です。

また、sizeof演算子を使うと、変数や型が何バイト使っているのかを確認できます。値、サイズ、アドレスを一緒に表示すると、変数がメモリ上にどのように存在しているのかをイメージしやすくなります。

アドレスを理解すると、ポインタの考え方が見えやすくなります。ポインタはアドレスを保存する変数であり、配列や関数、メモリ操作など、C言語の重要な機能と深く関係しています。

まずは、変数には値だけでなくアドレスもあること、&演算子で場所を取得できること、sizeofで大きさを調べられることをしっかり押さえておきましょう。