ホーム » スタッフ » 斉藤徹 » 講義録 » 情報構造論 » ポインタと番地の理解

2018年9月
« 8月   10月 »
 1
2345678
9101112131415
16171819202122
23242526272829
30  

最近の投稿(電子情報)

アーカイブ

カテゴリー

ポインタと番地の理解

リスト構造とかのプログラミングでは、ポインタが使われるが、番地とポインタをうまく理解していないと、どのような処理をしているのか理解しづらいはず。

今回の補講では、ポインタを理解してもらう。

以下では、ポインタを使った処理(前半)を見て、ポインタの動きを考える。理解できていなければ、同じ処理をポインタ無し、番地を意識させる memory[] 配列による記述(後半)で、動きを追って2つのプログラムが同じ挙動を表している…という説明の繰り返しで、ポインタの理解を図る。

単純な変数の加算

プログラムで、「 c = a + b ; 」と書いてあったら、メモリの「変数aの番地の中身」と「変数bの番地の中身」を加えて、結果を「変数cの番地」に保存する。

// 変数 a と 変数b の加算
int a = 11 ;
int b = 22 ;
int c ;
c = a + b ;
// 同じ処理をメモリの番地のイメージを示す。
int memory[ 1000 ] = { 0 , 0 , 0 , 11 , 22 , 0 , 0 } ;
#define ADDR_A 3
#define ADDR_B 4
#define ADDR_C 5
memory[ ADDR_C ] = memory[ ADDR_A ] + memory[ ADDR_B ] ;

ポインタのイメージ

// ポインタの処理
int a = 11 ;
int b = 22 ;
int*p ;
p = &a ;
(*p)++ ;
p = &b ;
(*p)++ ;
// 同じ処理をメモリ番地のイメージで
int memory[ 1000 ] = { 0 , 0 , 0 , 11 , 22 , 0 , 0 } ;
#define ADDR_A 3
#define ADDR_B 4
int p ;              // int *p ;
p = ADDR_A ;         // p = &a ;
memory[ p ]++ ;      // (*p)++ ;
p = ADDR_B ;         // p = &b ;
memory[ p ]++ ;      // (*p)++ ;

ポインタ渡し

// ポインタ引数による値の交換
void swap( int*x , int*y ) {
   int tmp = *x ;
   *x = *y ;
   *y = tmp ;
}
void main() {
   int a = 11 ;
   int b = 22 ;
   swap( &a , &b ) ;
}
// 同じ処理をメモリ番地のイメージで。
int memory[ 1000 ] = { 0 , 0 , 0 , 11 , 22 , 0 , 0 } ;
#define ADDR_A 3
#define ADDR_B 4
void swap( int x , int y ) {    // void swap( int*x , int*y ) {
   int tmp = memory[ x ] ;      //   int tmp = (*x) ;
   memory[ x ] = memory[ y ] ;  //   (*x) = (*y) ;
   memory[ y ] = tmp ;          //   (*y) = tmp ;
}                               // }
void main() {
   swap( ADDR_A , ADDR_B ) ;    // swap( &a , &b ) ;
}

上記のポインタの説明では、番地をintで表現しているから、型の概念が曖昧になりそう。
本当は、以下のように pointer 型を使って説明したいけど、補講の学生に typedef は、混乱の元だろうな。ひとまず、ここまでのポインタのイメージを再学習するネタを見てもらってからなら、typedef int pointer してもいいかな?

typedef int pointer ;
void swap( pointer x , pointer y ) {
   int tmp = memory[ x ] ;
   memory[ x ] = memory[ y ] ;
   memory[ y ] = tmp ;
}

プログラミングでは、型の理解が重要。たとえ、Python,Ruby といった型宣言の無い言語でも、どんなデータなのかを意識して書く必要がある。

理解の確認

// 以下のプログラムの実行結果は?
void foo( int x ) {
   x++ ;
}
void bar( int*p ) {
   (*p)++ ;
}
void main() {
   int a = 111 ;
   foo( a ) ;  // a の中身は?
   bar( &a ) ; // a の中身は?
}
// 同じ処理を
typedef int pointer ;
int memory[ 1000 ] = { 0 , 0 , 0 , 111 , 0 , 0 } ;
#define ADDR_A 3
void foo( int x ) {
   _______________________ ;
}
void bar( pointer p ) {
   _______________________ ;
}
void main() {
   foo( ________________ ) ; // memory[ ADDR_A ] の中身は?
   bar( ________________ ) ;  // memory[ ADDR_A ] の中身は?
}

ポインタと配列

// ポインタの移動
int sum = 0 ;
int array[ 3 ] = { 11 , 22 , 33 } ;
int*p ;
p = array ;
sum += *p ;
p++ ;
sum += *p ;
p++ ;
sum += *p ;
// 同じ処理をメモリ番地のイメージで
typedef int pointer ;
int memory[ 1000 ] = { 0 , 0 , 0 , 11 , 22 , 33 , 0 , 0 } ;
#define ADDR_SUM 2
#define ARRAY 3
pointer p ;                          // int*p ;
p = ARRAY ;                          // p = array ;
memory[ ADDR_SUM ] += memory[ p ] ;  // sum += (*p) ;
p++ ;                                // p++ ;
memory[ ADDR_SUM ] += memory[ p ] ;  // sum += (*p) ;
p++ ;                                // p++ ;
memory[ ADDR_SUM ] += memory[ p ] ;  // sum += (*p) ;

理解の確認

整数配列にデータが並んでいる。数字は0以上の数字で、データ列の後ろには必ず0が入っているものとする。配列の先頭から0を見つけるまでの値を合計する関数を作れ。

int sum( int*p ) {
   s = 0 ;
   for( __________ ; __________ ; __________ ) {
      ____________________ ;
   }
   return s ;
}
int array_a[ 4 ] = { 11 , 22 , 33 , 0 } ;
int array_b[ 5 ] = { 4 , 3 , 2 , 1 , 0 } ;
void main() {
   printf( "%d\n" , sum( array_a ) ) ; // 66 を表示
   printf( "%d\n" , sum( brray_b ) ) ; // 10 を表示
   printf( "%d\n" , sum( array_a + 1 ) ) ; // 何が表示される?
}

リスト構造

では、最後のシメということで、リスト構造でのポインタのイメージの確認。

// リストを次々たどる処理
struct List {
   int          data ;
   struct List* next ;
} ;
struct List* cons( int x , struct List* p ) {
   struct List* ans =
      (struct List*)malloc( sizeof( struct List ) ) ;
   if ( ans != NULL ) {
      ans->data = x ;
      ans->next = p ;
   }
   return ans ;
}
void main() {
   struct List* top =
      cons( 11 , cons( 22 , cons( 33 , NULL ) ) ) ;
   struct List* p ;
   for( p = top ; p != NULL ; p = p->next ) {
      printf( "%d¥n" , p->data ) ;
   }
}
// メモリのイメージで
typedef int pointer ;
int memory[ 1000 ] = {
   0  , 0 ,
   22 , 6 ,
   11 , 2 ,
   33 , 0 ,
   0  , 0 ,
} ;
#define OFFSET_DATA 0
#define OFFSET_NEXT 1
void main() {
   pointer p ;
   for( p = 4 ; p != 0 ; p = memory[ p + OFFSET_NEXT ] ) {
      printf( "%d¥n" , memory[ p + OFFSET_DATA ] ) ;
   }
}