Comments
Description
Transcript
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 文字を含まないものとする。