第 8 回 - 構造体(2)

[5/16, 2006 H.Aman]
67x27(1669bytes)   67x27(1669bytes) 【課題 2】

例題 2

例題 1 で説明した関数 printVector を次の条件に従って書き換えなさい:
  • 仮引数をポインタ変数に変更
  • ベクトルの表示機能はそのまま

コーディング例

     1	/*
     2	 * プログラミング演習 第 8 回
     3	 * [例題 2]
     4	 * (C) 2006 Hirohisa AMAN <aman@cs.ehime-u.ac.jp, aman@computer.org>
     5	 */
     6	#include <stdio.h>
     7	
     8	/* 構造体 vector の宣言 */
     9	struct vector {
    10	  int x;
    11	  int y;
    12	};
    13	
    14	/* ベクトル表示のための関数 : ポインタ版 */
    15	void printVector( struct vector* v ){
    16	
    17	  printf("( %d, %d )\n", v->x, v->y );
    18	
    19	}
    20	
    21	int main(void){
    22	  struct vector data;
    23	
    24	  scanf("%d %d", &data.x, &data.y);
    25	
    26	  printVector(&data); /* 呼び出し方法に注意 */
    27	
    28	  return 0;
    29	}
     
※左端の数字は行番号であり,ソースコードには含まれない点に注意!

コンパイル & 実行例

     $ gcc example8_2.c [Enter]
     $ ./a.out [Enter]
     3 12 [Enter]
     ( 3, 12 )
     

解説

ポインタ変数は, ある変数のメモリ上での番地 を格納するための変数である. データ(変数の値)ではなく, データのありか を指し示すために使われるという点が重要である.
※少し詳しい説明: 後期の「データ構造とアルゴリズム」で使用予定の説明スライド

一般に,ポインタ変数の宣言は,参照先データの型に依存する.
例えば,int 型変数の番地を格納したい場合, ポインタ変数は次のように宣言する(変数名を p としておく):

int* p;
この例題の場合,struct vector 型変数の番地を格納することになるので,
struct vector* v;
という宣言になる(15行目参照).

ポインタ変数の前に * (アスタリスク)を付けると, 参照先データの実体にアクセスできる.
したがって, *v が目的の構造体データになり,その中の x と y はそれぞれ (*v).x(*v).y になる.
ただ,C 言語ではこのようなポインタを使った構造体へのアクセスが頻繁に使われる(例えば,UNIX 系 OS のソースプログラムでは頻繁に見られる)こともあり, 特別に

v->x
v->y
という書き方も許されている. こちらの方が直感的(図で描いたイメージに近い)ということもあり, 一般的に使われている(17行目参照).

関数 printVector を呼び出す側(main 関数側)では, 構造体の 番地 を指定することになる. 変数の番地は, 変数の前に &(アンパサンド)を付けることで調べることができる. したがって,この場合は &data となっている(26 行目参照).

関数の引数をポインタにするメリット

既に知っている通り, 関数を呼び出す際に引数が指定されていると その内容がコピー される. この「コピー」という点が重要である.
一般に構造体では複数個の変数がひとかたまりになっている. つまり,コピーにはそれだけコスト (その分だけメモリ,時間,手間が) かかる.
一方,引数をポインタにしておくと, コピーは(メモリ上での)番地のみ で済む. 番地であるがゆえに, 構造体の内部構成がどれだけ複雑であっても, どれだけ多くのメモリを必要としようとも関係なく一定量である.