Download presentation
Presentation is loading. Please wait.
Published byりさこ たかはし Modified 約 5 年前
1
第7回 http://www.fit.ac.jp/~matsuki/lecture.html
プログラミングⅡ 第7回
2
連絡 小テスト 授業開始直後と終了直前に小テストを2行います 授業開始直後テスト(復習テスト)⇒前回の復習
授業終了直前テスト(本テスト)⇒その日の内容の復習 本テストの成績(正答率)が50%以下の場合は、宿題を出します(レポートとは別)。 宿題を決められた期日までに提出しない場合は、欠席扱いにします
3
宿題の提出について 以下の場合は、出題日の出席を欠席とする 締切日を過ぎた場合 正解率が7割未満の場合
提出は、PDFファイルを印刷して、それに答えを書いて提出すること。
4
今日の内容 ポインタ(応用編) 配列とポインタの関係 引数と配列 文字列とポインタ 標準ライブラリ 文字列を扱う関数紹介 動的メモリ確保
strlen関数, strcpy関数, strcat関数, strcmp関数 動的メモリ確保 malloc関数, free関数
5
配列とポインタの関係
6
配列要素のアドレスを知る 配列は,連続したアドレスに存在する 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) 配列要素も&を付けることで,各要素のアドレスを知ることができる
7
サンプルプログラム 実行例 #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])のアドレスを表す
8
配列名が表すものとは? 配列名は,実は 配列の先頭要素のアドレス を表している int test[5]; &test[0] test
配列の先頭要素のアドレス を表している int test[5]; &test[0] test (どちらも同じアドレス) 配列名で,配列の先頭要素のアドレスを表せる!
9
サンプルプログラム 実行例 #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 「配列名」は配列の 先頭要素のアドレスを 表す
10
配列名から先頭要素の値を知る 配列名は,配列の先頭要素のアドレスを格納しているポインタと同じはたらきを持つ ポインタ 配列
宣言 int *pA; アドレス pA 参照 *pA 宣言 int test[5]; アドレス test 参照 *test これらは,先頭要素についてのみ
11
サンプルプログラム 実行例 #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 配列名に*を付けると・・・
12
test &test[0] *test test[0] 配列名から先頭要素の値を知る 整理しましょう! 配列の先頭要素のアドレス
配列の先頭要素の値 *test test[0]
13
多バイト変数の場合,バイト単位で順番が逆になる(リトルエンディアン)
型の大きさとメモリ上の配置 型 変数1つが使うメモリサイズ 使える値の範囲(メモリ上での表記) char 1バイト 0x00~0xFF int 4バイト 0x ~0xFFFFFFFF double 8バイト 0x ~0xFFFFFFFFFFFFFFFF char A = 5; char B = 0xAF; int C = 0x1A2B3C4D; int D = 5; int型変数なので0x と同じ 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 多バイト変数の場合,バイト単位で順番が逆になる(リトルエンディアン)
14
配列データのメモリ上の配置 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]
15
ポインタ演算のしくみ 演算子 ポインタ演算の例(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
16
括弧を付けずに*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を足すことになる 先頭要素の「次の」の要素の値
17
ポインタ演算のしくみ まとめましょう どちらも同じ意味 *test test[0] 配列の先頭要素の値 *(test + 1)
配列の2番目の要素の値 *(test + 2) test[2] 配列の3番目の要素の値 ・・・ どちらも同じ意味
18
配列名を使うときの注意 配列とポインタの違う点がある 配列名で表されるポインタに,アドレスを代入することはできない
配列名で表されるポインタに,アドレスを代入することはできない #include <stdio.h> main() { int test[5] = {10, 20, 30, 40, 50}; int x = 3; test = &x; printf(“test[0]は%d\n”, test[0]); } アドレスを代入することはできない
19
引数と配列
20
配列を引数として使う #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は,引数に配列を使うことを宣言 配列名(先頭要素のアドレス)を実引数とする 次頁へつづく
21
配列を引数として使う /* 関数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 を利用
22
配列を引数として使う 先頭要素のアドレス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配列は すべて参照できるから
23
ポインタを引数として使う 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関数は,何も変更せずにそのまま使える
24
ポインタを引数として使う 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
25
ポインタに[ ]演算子を使う ポインタに[]演算子を使って,配列のように表記する []演算子: A[B] とは *(A+B)と同じこと
*(pT + 2) pT[ 2 ]
26
ポインタに[ ]演算子を使う もちろん,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関数は,何も変更せずにそのまま使える
27
文字列とポインタ
28
文字列をポインタで扱う 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 );
29
文字列をポインタで扱う char *str = “Hello”; str char型へのポインタとしても、文字列を扱える
0x12FF20 ポインタで文字列を初期化 str ‘H’ ‘e’ ‘l’ ‘l’ ‘o’ ‘\0’ 0x12FF20 番地 printf(“文字列は %s です\n”, str );
30
配列とポインタの違い エラー OK 配列名(先頭要素のアドレス)を変更することは、できない。 ポインタは、変更することができる。
char str[] = “Hello”; str = “Good Bye”; エラー 先頭要素のアドレスは変えられない char *str = “Hello”; str = “Good Bye”; OK
31
文字を格納する場所(メモリ領域)は確保していない
配列とポインタの違い scanf関数などで文字列を入力する場合、入力される文字を格納するメモリ領域が必要! 99文字文のメモリ領域を確保している char str[100]; scanf(“%s”, str ); OK 文字を格納する場所(メモリ領域)は確保していない char *str; scanf(“%s”, str ); エラー
32
文字列を扱う配列の配列 複数の文字列を配列でまとめて管理したい! “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文字以内
33
文字列を指すポインタの配列 複数の文字列を配列でまとめて管理したい! “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]);
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.