第7回 http://www.fit.ac.jp/~matsuki/lecture.html プログラミングⅡ 第7回 http://www.fit.ac.jp/~matsuki/lecture.html.

Slides:



Advertisements
Similar presentations
第 10 回 宿題 出題日: 12 月 14 日 締切日: 12 月 21 日. 提出について 以下の場合は、出題日の出席を欠席とする 締切日を過ぎた場合 正解率が 7 割未満の場合 提出は、 PDF ファイルを印刷して、それに答 えを書いて提出すること。
Advertisements

復習 配列変数の要素 5は配列の要素数 これらの変数をそれぞれ配列の要素と呼ぶ この数字を配列の添え字,またはインデックスと呼ぶ
復習 配列変数の要素 5は配列の要素数 これらの変数をそれぞれ配列の要素と呼ぶ この数字を配列の添え字,またはインデックスと呼ぶ
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第3回 配列(1) 情報・知能工学系 山本一公
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
ポインタ プログラミング入門2 第10回 芝浦工業大学情報工学科 青木 義満
プログラミング入門2 第4回 配列 for文 変数宣言 初期化
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
配列(2) 第10回[平成15年6月26日(木)]:PN03-10.ppt 今日の内容 1 素数を求める(教科書の例):復習
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
プログラミング演習Ⅱ 第12回 文字列とポインタ(1)
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
プログラミング基礎I(再) 山元進.
第13回 プログラミングⅡ 第13回
12: コマンドライン引数 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
第8回 プログラミングⅡ 第8回
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
理由:文字数より要素数の多い配列を用いた時に,文字列の最後を示すため
構造体.
精密工学科プログラミング基礎Ⅱ 第3回資料 今回の授業で習得してほしいこと: 2次元配列の使い方 (前回の1次元配列の復習もします.)
第10回 プログラミングⅡ 第10回
C言語講座 第3回 ポインタ、配列.
プログラミング論 関数ポインタ と 応用(qsort)
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
第11回 宿題 出題日:12月21日 締切日:1月7日(木).
精密工学科プログラミング基礎 第10回資料 (12/18実施)
プログラミング 4 記憶の割り付け.
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
第10章 これはかなり大変な事項!! ~ポインタ~
プログラミング入門2 第8回 ポインタ 情報工学科 篠埜 功.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
プログラミング入門2 第11回 情報工学科 篠埜 功.
前回の練習問題.
プログラミング入門2 第11回 情報工学科 篠埜 功.
アルゴリズムとデータ構造 補足資料5-1 「メモリとポインタ」
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
第11回 プログラミングⅡ 第11回
P n ポインタの基礎 5 q m 5 7 int* p; int 型の変数を指すポインタ int* q; int 型の変数を指すポインタ int n=5, m=7; int 型の変数 int array[3]; int* pArray[3]; p = &n; ポインタにアドレスを代入しているのでOK.
09: ポインタ・文字列 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
プログラミング基礎B 文字列の扱い.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
精密工学科プログラミング基礎Ⅱ 第5回資料 今回の授業で習得してほしいこと: 構造体 (教科書 91 ページ)
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング入門2 第9回 ポインタ 情報工学科 篠埜 功.
文字列へのポインタの配列 static char *lines[MAXLINES]; lines[0] NULL
情報基礎演習B 後半第2回 担当 岩村 TA 谷本君.
プログラミング論 ポインタ
09: ポインタ・文字列 C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページを開いておく こと
11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数
ネットワーク・プログラミング Cプログラミングの基礎.
第5回 プログラミングⅡ 第5回
プログラミング入門2 第5回 配列 for文 変数宣言 初期化
プログラミング 4 文字列.
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
2005年度 データ構造とアルゴリズム 第2回 「C言語の復習:配列」
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング演習II 2004年11月 2日(第3回) 理学部数学科・木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
情報処理Ⅱ 小テスト 2005年2月1日(火).
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
12: コマンドライン引数 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
Presentation transcript:

第7回 http://www.fit.ac.jp/~matsuki/lecture.html プログラミングⅡ 第7回 http://www.fit.ac.jp/~matsuki/lecture.html

連絡 小テスト 授業開始直後と終了直前に小テストを2行います 授業開始直後テスト(復習テスト)⇒前回の復習 授業終了直前テスト(本テスト)⇒その日の内容の復習 本テストの成績(正答率)が50%以下の場合は、宿題を出します(レポートとは別)。 宿題を決められた期日までに提出しない場合は、欠席扱いにします

宿題の提出について 以下の場合は、出題日の出席を欠席とする 締切日を過ぎた場合 正解率が7割未満の場合 提出は、PDFファイルを印刷して、それに答えを書いて提出すること。

今日の内容 ポインタ(応用編) 配列とポインタの関係 引数と配列 文字列とポインタ 標準ライブラリ 文字列を扱う関数紹介 動的メモリ確保 strlen関数, strcpy関数, strcat関数, strcmp関数 動的メモリ確保 malloc関数, free関数

配列とポインタの関係

配列要素のアドレスを知る 配列は,連続したアドレスに存在する a y &a &test[0] &test[1] 80 60 73 10 28 a test[0] test[1] test[2] test[3] test[4] y ・・・ ・・・ アドレス⇒0x05 ・・・・ 0x13 0x14 0x15 0x16 0x17 変数aのアドレスを表す(0x05) &a &test[0] &test[1] 先頭の要素(test[0])のアドレスを表す(0x13) 2番目の要素(test[1])のアドレスを表す(0x14) 配列要素も&を付けることで,各要素のアドレスを知ることができる

サンプルプログラム 実行例 #include <stdio.h> main() { int test[5] = {80, 60, 55, 22, 75}; printf(“test[0]の値:%d\n”, test[0]); printf(“test[0]のアドレス:%p\n”, &test[0]); printf(“test[1]の値:%d\n”, test[1]); printf(“test[1]のアドレス:%p\n”, &test[1]); } test[0]の値: 80 test[0]のアドレス: 0012FF24 test[1]の値: 60 test[1]のアドレス: 0012FF28 先頭の要素(test[0])のアドレスを表す 2番目の要素(test[1])のアドレスを表す

配列名が表すものとは? 配列名は,実は 配列の先頭要素のアドレス を表している int test[5]; &test[0] test      配列の先頭要素のアドレス                      を表している int test[5]; &test[0]     test (どちらも同じアドレス) 配列名で,配列の先頭要素のアドレスを表せる!

サンプルプログラム 実行例 #include <stdio.h> main() { int test[5] = {80, 60, 55, 22, 75}; printf(“test[0]の値: %d\n”, test[0]); printf(“test[0]のアドレス: %p\n”, &test[0]); printf(“testの値: %p\n”, test); } test[0]の値: 80 test[0]のアドレス: 0012FF24 testの値: 0012FF24 「配列名」は配列の 先頭要素のアドレスを 表す

配列名から先頭要素の値を知る 配列名は,配列の先頭要素のアドレスを格納しているポインタと同じはたらきを持つ ポインタ 配列 宣言  int *pA; アドレス  pA 参照  *pA 宣言  int test[5]; アドレス  test 参照  *test これらは,先頭要素についてのみ

サンプルプログラム 実行例 #include <stdio.h> main() { int test[5] = {80, 60, 55, 22, 75}; printf(“test[0]の値: %d\n”, test[0]); printf(“test[0]のアドレス: %p\n”, &test[0]); printf(“testの値: %p\n”, test); printf(“*testの値: %d\n”, *test); } test[0]の値: 80 test[0]のアドレス: 0012FF24 testの値: 0012FF24 *testの値: 80 配列名に*を付けると・・・

test &test[0] *test test[0] 配列名から先頭要素の値を知る 整理しましょう! 配列の先頭要素のアドレス 配列の先頭要素の値 *test test[0]

多バイト変数の場合,バイト単位で順番が逆になる(リトルエンディアン) 型の大きさとメモリ上の配置 型 変数1つが使うメモリサイズ 使える値の範囲(メモリ上での表記) char 1バイト 0x00~0xFF int 4バイト 0x00000000~0xFFFFFFFF double 8バイト 0x0000000000000000~0xFFFFFFFFFFFFFFFF char A = 5; char B = 0xAF; int C = 0x1A2B3C4D; int D = 5; int型変数なので0x00000005 と同じ 1 2 3 4 5 6 7 8 9 A B C D E F 0000 10 01 05 1B 1C 14 55 90 79 AF 29 A0 B0 C0 EE 5F 0010 80 60 50 88 0F 54 9E 28 13 07 56 11 45 22 0020 7C 7D 00 4D 3C 2B 1A 81 多バイト変数の場合,バイト単位で順番が逆になる(リトルエンディアン)

配列データのメモリ上の配置 char X[3] = {0x1A, 0x1B, 0x1C}; int Y[3] = {0x1A2B3C4D, 7, 8}; X[0] X[1] X[2] 1 2 3 4 5 6 7 8 9 A B C D E F 0000 10 01 1A 1B 1C 14 55 90 79 AF 29 A0 B0 C0 EE 5F 0010 80 60 50 88 0F 54 9E 28 13 07 56 11 45 22 0020 7C 7D 4D 3C 2B 00 08 81 05 Y[0] Y[1] Y[2]

ポインタ演算のしくみ 演算子 ポインタ演算の例(pはポインタ) + p+1 pが指している要素の次の要素のアドレスを得る - p-1 ++ p++ pが指す要素を1つ先に進める(次の要素に設定) -- p-- pが指す要素を1つ前に戻す(1つ前の要素に設定) int p[5] = {10, 20, 30, 40, 50}; p[0] p[1] p[2] p[3] p[4] ・・・ ・・・ pはint型配列 (要素1つは4バイト) ↓ もし,pが0x12ならば, p+1は0x16となる アドレス⇒ ・・・・ 0x12 0x16 0x1A 0x1E 0x22 p p+1 p+2 p+3 p+4

括弧を付けずに*test+1とすると,*testに1を足すことになる サンプルプログラム 実行例 #include <stdio.h> main() { int test[5] = {80, 60, 55, 22, 75}; printf(“test[0]の値: %d\n”, test[0]); printf(“test[0]のアドレス: %p\n”, &test[0]); printf(“testの値: %p\n”, test); printf(“test+1の値: %p\n”, test+1); printf(“*(test+1)の値: %d\n”, *(test+1)); } 4バイト増 test[0]の値: 80 test[0]のアドレス: 0012FF24 testの値: 0012FF24 test+1の値: 0012FF28 *(test+1)の値: 60 先頭要素の「次の」の要素のアドレス 括弧を付けずに*test+1とすると,*testに1を足すことになる 先頭要素の「次の」の要素の値

ポインタ演算のしくみ まとめましょう どちらも同じ意味 *test test[0] 配列の先頭要素の値 *(test + 1) 配列の2番目の要素の値 *(test + 2) test[2] 配列の3番目の要素の値 ・・・ どちらも同じ意味

配列名を使うときの注意 配列とポインタの違う点がある 配列名で表されるポインタに,アドレスを代入することはできない    配列名で表されるポインタに,アドレスを代入することはできない #include <stdio.h> main() {  int test[5] = {10, 20, 30, 40, 50};  int x = 3;  test = &x;  printf(“test[0]は%d\n”, test[0]); } アドレスを代入することはできない

引数と配列

配列を引数として使う #define NUM 5 int sum( int t[] ); 関数sumは,引数に配列を使うことを宣言 #include <stdio.h> #define NUM 5 /* sum関数のプロトタイプ宣言 */ int sum( int t[] ); main() { int test[NUM]; int i; int answer; printf(“%d人のテストの点数を入力してください\n”, NUM); for (i=0; i< NUM; i++) { scanf(“%d”, &test[i]); } answer = sum( test ); printf(“%d人の合計点は%d点です\n”, answer); マクロ宣言 ソースコード中のNUMはすべて「5」で置換 関数sumは,引数に配列を使うことを宣言 配列名(先頭要素のアドレス)を実引数とする 次頁へつづく

配列を引数として使う /* 関数sumの定義 */ int sum( int t[] ) { int i; int result; result = 0; for (i=0; i< NUM; i++) { result += t[ i ]; } return result; 大括弧を忘れないように! 仮引数に配列 t を使うことにしている 実行例 5人のテストの点数を入力してください 80 ↵ 60 ↵ 55 ↵ 22 ↵ 75 ↵ 5人の合計は292点です 配列 t を利用

配列を引数として使う 先頭要素のアドレス1個を渡すだけ! 80 60 55 22 75 main() { int test[5]; : int answer = sum( test );    : } test[0] test[1] test[2] test[3] test[4] 0x12FF20 int sum( int t[] ) { : } 先頭要素のアドレス1個を渡すだけ! これだけあれば,test配列は すべて参照できるから

ポインタを引数として使う main関数は,何も変更せずにそのまま使える 引数をポインタとして使うことも可能 ポインタ演算を用いて利用 /* 関数sumのプロトタイプ宣言 */ int sum( int *pT ); ・・・・ /* 関数sumの定義 */ int sum( int *pT ) { int i; int result; result = 0; for (i=0; i< NUM; i++) { result += *( pT + i ); } return result; 引数をポインタとして使うことも可能 ポインタ演算を用いて利用 main関数は,何も変更せずにそのまま使える

ポインタを引数として使う pT 先頭要素のアドレス1個を渡すだけ! 80 60 55 22 75 main() { int test[5]; : int answer = sum( test );    : } test[0] test[1] test[2] test[3] test[4] main関数が行うことは変わっていない 0x12FF20 int sum( int *pT ) { : } 先頭要素のアドレス1個を渡すだけ! 0x12FF20 pT

ポインタに[ ]演算子を使う ポインタに[]演算子を使って,配列のように表記する []演算子: A[B] とは *(A+B)と同じこと *(pT + 2) pT[ 2 ]

ポインタに[ ]演算子を使う もちろん,main関数は,何も変更せずにそのまま使える []演算子を利用することも可能! /* 関数sumのプロトタイプ宣言 */ int sum( int *pT ); ・・・・ /* 関数sumの定義 */ int sum( int *pT ) { int i; int result; result = 0; for (i=0; i< NUM; i++) { result += pT[ i ]); } return result; []演算子を利用することも可能! もちろん,main関数は,何も変更せずにそのまま使える

文字列とポインタ

文字列をポインタで扱う char str[] = “Hello”; 文字列とは、なんだったでしょうか? ⇒答え: char型の配列 配列で文字列を初期化 ‘H’ ‘e’ ‘l’ ‘l’ ‘o’ ‘\0’ str[0] str[1] str[2] str[3] str[4] str[5] printf(“文字列は %s です\n”, str );

文字列をポインタで扱う char *str = “Hello”; str char型へのポインタとしても、文字列を扱える 0x12FF20 ポインタで文字列を初期化 str ‘H’ ‘e’ ‘l’ ‘l’ ‘o’ ‘\0’ 0x12FF20 番地 printf(“文字列は %s です\n”, str );

配列とポインタの違い エラー OK 配列名(先頭要素のアドレス)を変更することは、できない。 ポインタは、変更することができる。 char str[] = “Hello”; str = “Good Bye”; エラー 先頭要素のアドレスは変えられない char *str = “Hello”; str = “Good Bye”; OK

文字を格納する場所(メモリ領域)は確保していない 配列とポインタの違い scanf関数などで文字列を入力する場合、入力される文字を格納するメモリ領域が必要! 99文字文のメモリ領域を確保している char str[100]; scanf(“%s”, str ); OK 文字を格納する場所(メモリ領域)は確保していない char *str; scanf(“%s”, str ); エラー

文字列を扱う配列の配列 複数の文字列を配列でまとめて管理したい! “Hello” “Good Bye” “Thank you” 3つの文字列からなる配列 char str[3][20] = {“Hello”, “Good Bye”, “Thank you”}; printf(“1つ目の文字列は%s\n”, str[0]); printf(“2つ目の文字列は%s\n”, str[1]); printf(“3つ目の文字列は%s\n”, str[2]); 1つの文字列は19文字以内

文字列を指すポインタの配列 複数の文字列を配列でまとめて管理したい! “Hello” “Good Bye” “Thank you” 3つの文字列を指すポインタ char *str[3] = {“Hello”, “Good Bye”, “Thank you”}; printf(“1つ目の文字列は%s\n”, str[0]); printf(“2つ目の文字列は%s\n”, str[1]); printf(“3つ目の文字列は%s\n”, str[2]);