スライド12

プログラミング演習Ⅱ
第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へ直接質問しに来ても構いません