プログラミング演習Ⅱ 第12回 文字列とポインタ(1) 情報・知能工学系 山本一公 [email protected] 前回の課題の解説・ポイント • 課題10-1 – ポインタ変数の扱いに慣れるのが目的 – 普通に平均を求めるだけ • 課題10-2 – こちらも、ポインタ変数の扱いに慣れるのが 目的 – 簡単なソートアルゴリズム • 選択ソート(一番小さい数を見つけて入れ替え) • バブルソート(隣合う2数を比較して入れ替え) 課題9の採点結果から • 課題9-1 – 配列に対して、要素毎のアドレスではなく、 先頭アドレスのみを表示しているケースが あった • 課題9-2 – OKになってない人は、テストが不十分です – 自分の書いたプログラムがどういう挙動にな るか、客観的に読めるようになりましょう 今日の内容 • 教科書 pp.248~253 • 文字列とポインタ • 「文字列とポインタ」となっていますが、 半分は「配列とポインタ」です 文字列リテラルとポインタ • 文字列リテラルは、メモリ上のどこかに確保さ れている • 文字列リテラルを評価すると、文字列の先頭文 字へのポインタが返される char str[] = “ABC”; /* &str[0] =‘A’のアドレス となる */ char *ptr = “123”; /* ptr = ‘1’のアドレス となる */ – 配列strの最初の要素が’A’、2番目の要素が’B’、3番目 の要素が’C’、4番目の要素が0で初期化される – ptrには、メモリ上のどこかに確保された文字列の先 頭アドレスが代入される • ptrが確保される場所と文字列リテラルの場所は無関係 配列とポインタの共通点 • 配列で使う添字演算子“[” “]”をポインタに 対しても使うことができる – 効果も同じ – ポインタを使って、配列と同じことができる char str[] = “ABC”; /* &str[0] =‘A’のアドレス となる */ char *ptr = “123”; /* ptr = ‘1’のアドレス となる */ – この場合、ptr[0]は‘1’ 、ptr[1]は‘2’、 ptr[2]は ‘3’である • C言語では、配列の先頭アドレスしか見ていない (要素数は無視)ので全く同じになる 配列とポインタの違い(1) • 配列変数はconst扱いなので、代入できない • ポインタ変数は代入できる char str[] = “ABC”; char *ptr = “123”; str = “DEF”; ptr = “456”; /* エラー! */ /* 正しい! */ – 配列変数への文字列リテラルの代入は、初期化の場 合のみ可能 – ポインタ変数の場合は、メモリ上のどこかに確保さ れる文字列リテラルの先頭文字へのポインタが代入 されるだけなのでプログラム中でも可能(Fig.11-3) 配列とポインタの違い(2) • 配列は、初期化時に要素数を指定できる ので、使用しない領域を確保しておくこ とが可能(Fig.11-4(a)) • ポインタはあくまでもアドレスを保持す るだけの単独の変数(Fig.11-4(b)) – 文字列リテラルは、文字列に必要な領域しか 確保されない 文字列の配列(1) • 文字列の配列は、配列の配列で実現する場合と、 ポインタの配列で実現する場合で(メモリ上の 実体が)かなり異なる • 配列による文字列の配列の場合 – C言語での配列の配列(2次元配列)は、 1次元配列を2次元的に扱っているだけ • char st[3][6] と定義されたものは実質 st[18] と同じ • st[2][5] は st[2 * 6 + 5] としてアクセスされる – すなわち、メモリ上には連続的に配置される 文字列の配列(2) • ポインタによる文字列の配列の場合 – ポインタ配列の要素は、それぞれの文字列リテラル の先頭アドレスを指すだけ – 文字列はそれぞれがメモリ上のどこかに存在する • 実際にどこに配置されるかは処理系依存 • Fig. 11-5 – 配列で実現する場合は、2次元配列の初期化と同様に、 間に0が埋め込まれている – ポインタの場合は0は入らないし、連続した領域に確 保されてもいない • ポインタの配列も、他の配列と同じように扱われるので、連 続した領域に確保される 共通点・相違点まとめ • 共通点 – 配列にもポインタにも添字演算子が使える • 書き方も使い方も結果も同じ • 区別なく使って特に問題ない • 相違点 – 配列の配列(2次元配列)による文字列の配列 • その実体が連続した領域に確保される(2次元配列なので、文字のないとこ ろには0が入る) • 配列なので、文字列の代入は初期化時しかできない。文字単位の置き換え はできる。 – ポインタの配列による文字列の配列 • ポインタは連続した領域に確保されるが、実体は必ずしも連続した領域に 確保されるわけではない • 要素を別の文字列リテラルで置き換えることができる。 今週の課題 1. 教科書p.253, List 11-5のプログラムを改造して、st[0][0]~st[2][5]、 pt[0][0]~pt[2][5]の各要素の内容とアドレスを表示するようにし、 「配列による文字列」と「ポインタによる文字列」のそれぞれの 文字列リテラルがメモリ上でどのように配置されているか調べよ。 文字列の内容はList 11-5から変更しないこと。 2. 1次元配列の先頭アドレスをポインタ配列に代入すれば、2次元配 列を実現できる。このようにして実現した2次元配列による行列の 掛け算を行う関数(行列a×行列bの結果を行列ansに入れる関数。a はn×m行列、bはm×l行列、ansはn×l行列とする)void mat_mul(int *ans[], int *a[], int *b[], int n, int m, int l)を作成せよ。main関数なども 作成して完成したプログラムを作ることとし、main関数の中で教 科書p.103, 演習5-7の配列を与えて関数を呼び出し掛け算を計算せ よ。 課題2のヒント(1) • ポインタ配列を関数に渡す int main(void) { … char *ptr[3] = {“123”, “4567”, “78910”}; … function(ptr); … void function(char *ptr[]) { … 課題2のヒント(2) • ポインタをポインタ配列に代入する int main(void) { … int a0[3], a1[3]; // 3要素の1次元配列 int *ptr[2]; // 2要素のポインタ配列 … ptr[0] = a0; ptr[1] = a1; // これで2×3の行列ができる // 行列要素の代入 ptr[0][0] = 1; ptr[0][1] = 2; ptr[0][2] = 3; ptr[1][0] = 4; ptr[1][1] = 5; ptr[1][2] = 6; … 課題2のヒント(3) • これ何の役に立つの? – 1次元配列は、malloc()関数によりプログラム中で可変 長の領域を確保できるので、これを応用することに よって任意の多次元可変長配列をプログラム中で作 ることができる int n, m, i; int **ptr; … // n × m の2次元配列を作る ptr = (int**)malloc(sizeof(int*) * n); // n次元のポインタ配列 for (i = 0; i < n; i++) { ptr[i] = (int*)malloc(sizeof(int) * m); // m次元の実配列 } レポートについて • 電子メールで提出 – 提出先は [email protected] – Subjectを「プログラミング演習2 課題11提出 番号・氏名 」とすること – C言語ソースファイルを添付する 学籍 • メールの本文には何も書かなくて良いです – ソースファイルの頭にコメントで以下の情報を入れる • 学籍番号・氏名 • プログラムの説明(どのように動くのか、工夫した点等) • 実行結果(長い場合は一部)を貼る – 提出締切は、1月30日(水) 12:00 (1週間後) 授業用Webサイト • URL: http://www.slp.cs.tut.ac.jp/~kyama/programming2/ – 課題のpdfファイルが置いてあります。 – 授業で使ったpptファイルを置いていきます。 • 質問メールは、以下のどちらかのアドレスまで – [email protected] – [email protected] • C-515へ直接質問しに来ても構いません
© Copyright 2024 ExpyDoc