情報処理II

情報処理Ⅱ
2005年11月18日(金)
本日学ぶこと


ポインタ(Pointer)
問題

文字列が回文("noon", "トマト" など)かどうか判定でき
る?
2
ポインタ

ポインタとは?


指し示すもの
配列とはまた別の,実用的かつ多様な型を作る一つの方式
(派生型)
y

*y
なぜ必要?


メモリ(特に,配列とヒープ)に効率よくアクセスできるから.
関数の仮引数によく使用されるから.
3
ポインタ変数の宣言(1)

ポインタ変数の宣言


char *p; と書くと,「char *」型の変数pを確保する.
• char* p; と書く流儀もある.
一括の宣言


char *p, *q; のように複数宣言できる.
char *p, q; とすると,qはchar型になる.
「*」は
演算子ではなく
型名の一部
4
オブジェクトとポインタの関係(1)

int x; int *y; とするとき,




x はint型の変数,またはその値
y はint *型のポインタ変数,またはその値(ポインタ値)
*y は「yの参照先」を表すint型の値
&x は「xの参照」を表すint *型のポインタ値
* と & は
単項演算子
(積や論理積
ではない)
x
&x
y
*y
5
オブジェクトとポインタの関係(2)

int z[10]; とするとき,



z[k] はint型の値(左辺値に
なり得る)
&z[k] および z+k は,
「z[k] の参照」を表す
int *型のポインタ値
ポインタ値としては,
z
z と z+0 と &z[0] は
同じ値となる.
演算子の優先順位に
注意すると,&z[k]は,
(&z)[k]ではなく
&(z[k])と同じ
配列変数は,参照時
にはポインタとなる!
z+0 z+1
z+9
z[0] z[1] … z[9]
&z[0]&z[1]
&z[9]
6
ポインタ変数の宣言(2)

初期化




○ int x; int *y = &x;
○ char a[10]; char *b = a;
○ char *p = "programming";
「初期化」と「式」とで型の違いに注意!


この b は a の
別名(alias)と
いう.
char *b = a;
⇒ a はchar *型
char *b; *b = a; ⇒ a はchar型
7
配列とポインタ(1)

int x[5]; int *y = x; とするとき,
x[3] と y[3] と *(x + 3) と *(y + 3) は同じオ
ブジェクトを表す.


左辺値になり得る(代入可能).
変数αが配列変数であってもポインタ変数であっても,α[i] と
*(α + i) は同じオブジェクトを表す.
• i は負でもよい.
• *(α + 0) は *α と書ける.
x[0] x[1] x[2] x[3] x[4]
y[0] y[1] y[2] y[3] y[4]
x
y
8
配列とポインタ(2)

int x[5]; int *y = x + 1; とするとき,
x[3] と y[2] は同じオブジェクトを表す.
x[0] x[1] x[2] x[3] x[4]
y[-1] y[0] y[1] y[2] y[3]
x
y

z
(同一型の)ポインタ同士の減算が可能.


上図で,y – x は 1. y – z は -3.
同一配列を参照している2つのポインタがどれだけ離れている
かを知ることができる.
9
同じオブジェクト?


ポインタ値は,printf関数の第1引数に「%p」を使用すれば
出力できる.
注意

2つのポインタ値が同じオブジェクトを指すことがあっても,そ
れらが同じ型かどうかはわからない.
• キャスト演算子でポインタ型の変換が可能.
• 実行時に型名を獲得する方法はない.
10
ポインタ変数にできて
配列変数にできないこと


(前提: int x[5]; int *y;)
代入



○ y = x;
× x = y;
整数との加減算


○ y++; や y -= 3;
× x++; や x -= 3;
x
x[0] x[1] x[2] x[3] x[4]
y
y+1
y+2
y+3
y+4
11
配列変数にできて
ポインタ変数にできないこと(1)

初期化していない状態での参照や代入


int x[10]; int *y; とするとき,
• ○ x[5] = 1;
• × y[5] = 1;
適切に指し示す先を設定しておけば,問題ない.
x
y
x[0] x[1] … x[9]
???
初期化していないとき,
それぞれの値は不明.
しかし代入はできる.
12
配列変数にできて
ポインタ変数にできないこと(2)

文字列の書換え

char x[ ] = "programming";
char *y = "programming";
とするとき,
• ○ x[7] = '\0';
• × y[7] = '\0';
• 配列変数 x の初期化は,文字列が配列値として格納され
るのに対して,ポインタ変数 y への代入は,(書換えられ
ない)文字列リテラルを参照するだけだから.
• ○ y = x; y[7] = '\0';
13
直感的には


配列変数は安定的
ポインタ変数は身軽
x
x[0] x[1] x[2] x[3] x[4]
y
y+1
y+2
y+3
y+4
y-1
y
y+1
y+2
y+3
y++;
14
回文判定

仕様




入力(文字列)は,ポインタ変数にあらかじめ参照させておく.
文字列が回文になっていれば「Yes」を,そうでなければ「No」
を出力する.
英字の大小を区別する.ASCIIコード以外の文字は使用しな
い.
case-sensitive という
例




"12321" : Yes
"123321" : Yes
"noon" : Yes
"Noon" : No
15
回文判定

1文字ずつ見ていく操作を
文字列の「走査(scan)」という
左と右から1文字ずつ見て


途中で一致していない箇所があれば,「No」
すべて一致し,参照位置が一致または交差すれば,「Yes」
'1'
p
'2'
'3'
'4'
q
'1'
'1'
'\0'
No
'\0'
Yes
r
'2'
'3'
'2'
'1'
16
回文判定:ポインタ変数は?

char *p = "12321";


char *q = p;



文字列走査では固定
文字列の先頭から始まる
文字列走査により1ずつ増えていく
char *r;


文字列の末尾から始まる
文字列走査により1ずつ減っていく
17