4.2 配列とポインタ

 配列とポインタ
ポインタはメモリのアドレスを表しているので、配列の要素を指すことも出来る。例えば、図
に示すように、 個の要素からなる $ 型配列 <?%@ が宣言されているとする。
# 言語では、配列名は配列の最初の要素の
アドレスを表すので、$ 型へのポインタ変数
i_ptr++;
< に < を代入すると、< は
100:
i_array[0]
<?@ へのポインタとなる。すなわち、
-< は <?@ を表すことになる。こ
こで、< に対してインクリメント演算を
施す <:: または ::< と、< は
104:
i_array[1]
次の整数 すなわち <?@ へのポイン
タとなる。$ 型の記憶領域は バイトなので、
これは < の値が 増えることを意味して
いる。
i_array[2]
108:
デクリメント演算子を施す場合は、インク
リメント演算子と逆のことが起こる。例えば、
< が <?@ を指しているときに <
112:
i_ptr
に対してデクリメント演算子を適用 <
または < すると、< は一つ前の整
i_ptr = i_array;
数 すなわち <?@ へのポインタとな
る。$ 型の記憶領域は バイトなので、これ
int i_array[3];
は < の値が 減ることを意味している。
int *i_ptr;
一般に、ポインタ と整数 との加減算で
は、 の値は >*
だけ増加または減少
することになる。したがって、例えば、< が
図 ) 配列とポインタ
<?@ を指している場合、.< : $/
は <?$@ を指すことになる。すなわち、この場合、-.< : $/ の値は、<?$@ と
なる。また、< は <?@ のアドレス、すなわち、<?@ へのポインタとなって
いるので、<?$@ の代わりに -.< : $/ と書くことが出来る。さらに、ポインタ変
数 < に対して、-.< : $/ の代わりに <?$@ と書いても良い。
プログラム例 は、ポインタ変数 を用いて配列 3:; に を代入 し、正
しく代入できていることを確認するプログラムである。
プログラム例 )&#'
*
<$ 7"
<
< + <$
&+ 7 !!'*
< + <!!
に必要なヘッダファイル
型の配列 <$ を宣言
型へのポインタ変数 < を宣言
ポインタ変数 < に、配列 <$ の先頭要素のアドレスを代入
+ ∼ . に対して、
< が指している場所へ を代入
次の整数が入っている場所を指すように < の値を更新
,
,
&+ 7 !!'
&3<$ 4" + 453 <$ "'
練習
+ ∼ . に対して、
と配列 <$ の 番目の要素を出力
例 のプログラムを、よく意味を考えながら実行せよ。レポート提出は不要。
に示すように、 が変数 へのポインタで
あり変数 の値が「」の時に「 A」を実行すると、ポインタ変数 の値 変数 のアドレス がポインタ変数 に代入 コピー され、図 のような状態になる。すなわち、
も変数 へのポインタとなり、B の値は「」となる。 も も同じ変数 へのポイ
ンタとなっているので、例えば、 の値やB の値を「」に変更するとB の値も「」に変化
する。
【ポインタのコピーとデータのコピー】 図
int val;
int *pta;
int *ptb;
// intᆺኚᩘva lࡢᐉゝ
// intᆺ࡬ࡢ࣏࢖ࣥࢱኚᩘptaࡢᐉゝ
// intᆺ࡬ࡢ࣏࢖ࣥࢱኚᩘptbࡢᐉゝ
pta = &val;
*pta = 5;
// ࣏࢖ࣥࢱኚᩘpta࡟ኚᩘvalࡢ࢔ࢻࣞࢫࢆ௦ධ
// ptaࡀᣦࡋ࡚࠸ࡿሙᡤval࡟㸳ࢆ௦ධ
val:
ptb = pta;
// ptaࡢ್ࢆptb࡟௦ධ࣏࢖ࣥࢱࡢ௦ධ
val:
5
pta:
pta:
ptb:
ptb:
5
(b)
(a)
図
int
int
int
int
iva;
ivb;
*pta;
*ptb;
pta = &iva;
ptb = &ivb;
*pta = 5;
) ポインタのコピー
// intᆺኚᩘivaࡢᐉゝ
// intᆺኚᩘivbࡢᐉゝ
// intᆺ࡬ࡢ࣏࢖ࣥࢱኚᩘptaࡢᐉゝ
// intᆺ࡬ࡢ࣏࢖ࣥࢱኚᩘptbࡢᐉゝ
// ࣏࢖ࣥࢱኚᩘpta࡟ኚᩘivaࡢ࢔ࢻࣞࢫࢆ௦ධ
// ࣏࢖ࣥࢱኚᩘptb࡟ኚᩘvalࡢ࢔ࢻࣞࢫࢆ௦ධ
// ptaࡀᣦࡋ࡚࠸ࡿሙᡤiva࡟㸳ࢆ௦ධ
iva:
*ptb = *pta;
// ptaࡀᣦࡋ࡚࠸ࡿሙᡤivaࡢ್ࢆ
// ptbࡀᣦࡋ࡚࠸ࡿሙᡤivb ࡟௦ධࢹ࣮ࢱࡢ௦ධ
5
iva:
pta:
pta:
ivb:
ivb:
ptb:
ptb:
(a)
5
5
(b)
図 ) データのコピー
一方、図 に示すように、 が変数 へのポインタ、 は変数 へのポインタであ
り変数 の値が「」の時に、
「B B」を実行すると、 が指している場所 の値が
が指している場所 に代入 コピー され、図 のような状態になる。この時、B の
値は「」であるが、 と の値は異なっているため、例えば、 の値やB の値を「」に
変更しても、 やB の値は変化せず「」のままである。
【ポインタや配列の注意点】 配列とポインタ変数はほぼ同じように用いることが出来るが、次の
点に注意する必要がある。
配列宣言ではデータの格納場所が確保されるので、すぐにでもデータを格納することが出
来る。
配列名は配列の先頭の要素を格納するアドレスを表す定数なので、配列名に別のアドレスを
代入することは出来ない
ポインタ変数の宣言では、ポインタの値 アドレス の格納場所が確保されるだけで、実際の
データの格納場所は確保されない。このため、
ポインタ変数を宣言しただけでは、ポインタの指し示す場所 アドレス にアクセスす
ることは出来ない
ポインタ変数には別のアドレスを代入することができる。
ポインタをコピー 代入 した場合、ポインタの値 アドレス がコピーされるだけであり、そ
のポインタが指し示すデータがコピーされるわけではない
【ヒント】 ポインタが分かりにくいときは、図を書いて考えると理解しやすくなる。
ポインタによる文字列処理
# 言語では、文字列は文字型の配列として扱うことができ、文字列の最後を示すために、最後に
バイトで表現さ
れる半角の英数字記号のことである。日本語のかなや漢字等は、 バイト以上で表現されるため、
この節で扱う文字の対象外とする。
はヌル文字 I9I が付加される。なお、この節で扱う「文字」とは、 文字が
プログラム例 G@H .;
G@H"
)&#'
*
+ !!" + 2C2
!!" + 22
!!" + 22
!!" + 22
!!" + 22
!!" + 252
!!" + 252
!!" + 22
&34
3
'
,
や記号定数 HIGG に必要なヘッダファイル 文字列を格納するための 型配列 " + 2C2 " + 22 ." + 22 7" + 22 :" + 22 0" + 252 &改行' 252 の代わりに や HIGG でもよい 252 以降は文字列には含まれない 文字型配列 の内容を文字列として出力 " や " で文字列を入出力する場合、書式制御文字として6 を用いる 。このプログラ
ムでは、文字型の配列 の先頭の要素から順に文字を代入しているが、最終的に配列名 が
文字列4
!!9"4 を指し示すことになる 図 参照。したがって、最後の $* では、文字列
4
!!9"4が出力され文字 =(以降)は出力されない。
:;
CDC
-D
: ;
CC
:;
CC
:;
CC
:;
CC
:;
C9"C
$ -
:;
C9C
:;
C4C
これ以降は
文字列に含まれない
図 ) 文字列と配列
プログラム例 は、" で読み込んだ文字列の長さを、文字型へのポインタ変数 を用いて
求めるプログラムである。図 参照
プログラム例 G@H .;
G@H"
に必要なヘッダファイル 文字列を格納するための 文字型配列 )&#'
*
文字型へのポインタを格納する変数 を宣言 文字列の長さを格納する変数 &34
3
'
+ + J&!! K+ 252'
,
文字型配列 に文字列を入力 に " へのポインタを代入 最初は を に初期化 が指しているところの文字が HIGG 文字で無い間、 を増やすとともに !!
長さ &' を 増やす 入力された文字列とその長さを出力。 &3「4
」の長さ + 453 '
str[0] str[1] str[2] str[3] str[4]
str[n]
str
pt:
図 ) ポインタを利用して文字列の長さを求める
プログラム例 は、" で文字型配列 に読み込んだ文字列を、文字型へのポインタ変数
と を用いて文字型配列 にコピーし、両者を出力するプログラムである。図 参照
% で文字列を読み込む場合、空白文字やタブ、改行文字は文字列の区切りと見なされるので、これらの文字を含む
文字列を読み込むことはできない。空白文字やタブ、改行文字も文字列の中の文字として扱いたい場合は 1$- を用い
て1文字ずつ読み込み配列に順次格納する等の方法を用いる。
プログラム例 G@H .;
G@H" G@H"
を使うために必要なヘッダファイル 文字列を格納する配列の宣言 )&#'
*
文字型へのポインタ と を宣言 &3文字列を入力して下さい53'
&34
3 '
文字列を配列 に入力 + ポインタ変数 に配列 の先頭 を代入 + ポインタ変数 に配列 の先頭 を代入 J& K+ 252'
が指している場所にある文字が252 でなければ !! + !!
が指している場所の文字を が指している場所に コピーし、 と のポインタを増加 + 252
文字列の最後を示す252 をコピー &3D 4
53
'
配列 の文字列を出力 &3>$ 4
53
'
配列 の文字列を出力 ,
stra
pta:
ptb:
strb
図 ) ポインタによる文字列のコピー
練習
演習
例 ∼ のプログラムを、意味をよく考えながら実行せよ。レポート提出は不要。
例えば「8$EF」のようにCEC で区切られて入力された氏名を、氏 8$ と名
F に分割して出力するプログラムをポインタを用いて作成せよ。
name
shi:
mei:
図 ) 文字列の分割
【ヒント】 まず、配列 "
にII で区切られた氏名を入れておく。次に、 型へのポインタ
# に配列 "
の先頭要素へのポインタを代入する。ポインタ が指す場所に格納され
ている文字がII で無い間、
を一つづつ増やすことにより文字II が格納されている場所を見
つける。そしてその文字IIポインタ が指している を JKK.I9I/ に置き換え を 増や
す。これにより、ポインタ が指す文字列は「氏」の部分、ポインタ が指す文字列は「名」
の部分となる。図 参照。
文字列定数は4
!!4のように、ダブルクォーテーションで文字列を囲って表す。
9を用いることにより、49"
!!4のように文字列の中に改行コードなどの制御コードを含ませ
ることもできる。文字列定数は、対応する文字コードの列が格納されている記憶領域へのポインタ
として扱われる。このことは、次のプログラムにより確かめることができる。
プログラム例
に必要なヘッダファイル )&#'
*
+ 3C3
&34
53'
,
文字型のポインタ変数 を宣言 C という文字列へのポインタを に代入 が指している文字列と改行コードを出力 演習
入力した二つの文字列 が等しければ と出力し、そうでなければ " と出力するプロ
グラムをポインタを用いて作成せよ。
演習
空白文字や F?G 文字を含まない文字列を つ入力し と とする、 が の部分文
字列なら
に対する の開始位置 の先頭から数えて何文字目から が始まるか を出力し、
が部分文字列として現れなければ を出力するプログラムをポインタを用いて作成せよ。
の中に複数回現れる場合は、最初に現れる位置を出力するものとする。たとえば、
-- で -- の時は と出力し、 -- で -- の時は と出力す
るものとする。また、 の長さを各々 とするとき、作成したプログラムの実行時間の概略
を を用いて理論的に考察せよ。
の中に
なお、 が
文字列は空白文字や 2 文字を含まないものとする。