第3回:アドレスとポインタ変数 (5/18)


第2回演習の解答例


今週は,機械系エンジニアが計測・制御を行う際によく利用する,アドレスとポインタ変数について勉強します。とくに,ポインタは,わからなくなる人が続出する難関です。

前提知識:「コンピュータの仕組み」「メモリ」「配列」「文字列」

キーワード:「アドレス」「ポインタ変数」「&」「*」


アドレス

プログラム中の変数や配列などの値は,コンピュータのメインメモリ(Random Access Memory,RAM,ラムと読む)上に記憶されている。このメモリには,場所を表す「アドレス」という連続した通し番号(整数の値)がついており,変数名とアドレスの組み合わせは,OS(Operating System, ここではWindows)により管理されている。
たとえば,C言語プログラム中で int a; と整数の変数を1つ定義すると,整数の値1個を格納する場所がメインメモリ上に確保され,a という名前を使ってこの場所に値を書き込んだり参照したりすることができるようになる。下の図ではアドレスは int型の変数 a が,4 バイト分のメモリを割り当てられていることを示す。
機械語では、このアドレスを直接用いてプログラミングするが、C言語では変数名aによりプログラミングできる。

では,ある変数に割り当てられた場所(メモリのアドレス,上の図では0x0000E008)を調べるには,どうしたらよいか。
C言語では,変数の名前の直前に「」を付けると,変数のアドレスを取り出すことができる。


#include <stdio.h>
 
void main()
{
    int a = 10;
    
    printf("aの 値  は,%d です¥n",  a); /*  普通に値を表示  */
    printf("aのアドレスは,%p です¥n", &a); /*  %p はアドレスを16進数8桁で表示する */
}
 

課題1

下記の変数のアドレスを調べ,答えなさい。

Hint: 配列のアドレスの例 &a[0]

この結果から,int, float, char, 配列などの各型がメモリ上でどれくらいの大きさかがわかる。


ポインタ変数

アドレスを扱うための特別な変数を,ポインタ変数と呼ぶ。「アドレス」はもちろん整数値であるが,変数の型に応じて割り当てられる領域が異なるので,ポインタも中身の型にあわせて定義する必要がある。

ポインタ変数の宣言

ポインタ変数を宣言するためには,目印として変数名の前に「」をつける。

int *p;

ここで,変数名は「p」であり,「*」は,ポインタ変数の目印である。

ポインタ変数にアドレスを代入する

アドレスを扱うポインタ変数の準備ができたら,早速アドレスを代入してみよう。

int a;
int *p;
p = &a;

課題2

char c;
int *p;
p = &c;

このコードは,エラーになる。理由を答えなさい。


ポインタ変数のアドレスに格納されている変数を操作する

ポインタ変数に格納されているアドレスの示す先に格納されている値を取り出すには,ポインタ変数の前に「」をつける。

b = *p;

 

大丈夫ですか?? 混乱していませんか?次のプログラムで確認しよう。


プログラム例:


#include <stdio.h>
 
void main()
{
    int a; /* 普通の変数の宣言 */ 
    int *p;   /* ポインタ変数の宣言 */
 
    a = 3;
    printf("aのアドレスは,%p です¥n", &a);  
 
    p = &a;
    printf("ポインタ変数pの値(=aのアドレス)は,%p です¥n", p);  
    printf("ポインタ変数pの指している値は,%d です¥n", *p);  
}

課題3

値の代入,アドレスの代入

    int a=2, b=3;
    int *pa, *pb;
    
    pa = &a;
    pb = &b;

のとき,次の二つの演算の違いを説明しなさい。
    
    a = b;
    
    pa = pb;
    

配列とポインタ(重要!)

C言語において配列は,メモリの連続した区間を使うという約束になっている。ここではポインタを使って,配列の要素を扱ってみよう。

例:


#include <stdio.h>
 
void main()
{
    int a[5] = {10, 20, 30, 40, 50};   /* 配列 */
    int b;
    int *p;   /* ポインタ変数 */
 
    p = &a[0];            /* 配列の先頭のアドレスを代入する */ 
 
    printf("配列 a の先頭のアドレスは,%p です¥n", p);
 
    printf("はじめに:p の指している値は,%d です¥n", *p);
 
    p += 2;    /* ポインタ変数に2を足すと,pは配列の先頭から2つ先を指す=a[2]のアドレス! */
 
    printf("ここでは:p の指している値は,%d です¥n", *p);
 
    b = *(p-1); /* ポインタの指しているアドレスのひとつ前隣の値をbに代入 */
    printf("b の中身は,%d です¥n", b);  
}

このように,ポインタ変数に足し算,引き算をすることにより,配列の要素に順次アクセスできる。
ポインタに1を足したときに何バイト先に進むかはポインタの指す変数の型により異なるが,ポインタ変数を宣言する時の型指定から自動的に計算される。


課題4

整数型配列に初期値として,(10,20,30,40,50)を設定したとき,全ての要素の値を表示するプログラムをポインタを用いて作成せよ。

ヒント:


#include <stdio.h>
 
void main()
{
    int a[5] = {10, 20, 30, 40, 50};
    int *p;
    int i;
 
    p = ???;
 
    for(i=0; i<5; i++) {
        printf(" 配列の %d 番目は???です¥n", ?????? );
    }
}

ポインタと文字列

前回学習した文字列も,ポインタを使って操作できます。

配列 ポインタ
#include <stdio.h>

void main()
    {
        char a[] = "ABC012";
        
        printf("文字列は%sです¥n", a);
    }
#include <stdio.h>

void main()
{
    char *pa = "ABC012";
 
    printf("文字列は%sです¥n", pa);
}

課題5

前回の課題3「キーボードから文字列を入力すると,何文字入力されたかを画面に表示するプログラムを作成せよ。」を,ポインタを用いて作成しなさい。