• 検索結果がありません。

ヒントスライドPPT プログラミング演習2 #prog2bkc net

N/A
N/A
Protected

Academic year: 2018

シェア "ヒントスライドPPT プログラミング演習2 #prog2bkc net"

Copied!
40
0
0

読み込み中.... (全文を見る)

全文

(1)

第 2 週

配列とポインタ

プログラミング演習2

(2)

ポインタ (pointer) とは

 変数,配列,関数などの(メモリ上の)アドレスを

指し示す

• ポインタ変数の中身はアドレス

「~へのポインタ」

 C言語を代表する非常に便利な機能

 メモリ空間をイメージすることが理解への早道

アドレスの理解

今日のキーワード

・メモリ空間

・アドレス

・ポインタ変数

・ポインタの演算

(3)

3

メモリとアドレス

CPU

Core(TM) 2 Quad プロセッサ 2.50GHz

メモリ

4GB デュアルチャネル DDR2-SDRAM メモ

(4)

メモリ空間とアドレス

4G バイト

= 0x0 ~ 0xffffffff

0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xffffffff

1 バイト

アドレスの先頭 アドレス

16 進数で表すことが多い

(5)

5

復習:変数

変数

• 数値や文字を格納する箱のようなもの

• 型によってサイズが違う

– char 型: 1 バイト

– short 型: 2 バイト

– int 型: 4 バイト(環境によって異な

る)

1 バイト

2バイト

4 バイト int main(void){

char c; short s; int num; ...

return 0; }

int main(void){ char c;

short s; int num; ...

return 0; }

型名 変数名

(6)

変数とアドレスの関係

int main(void){ int a;

a=100; ...

return 0; }

int main(void){ int a;

a=100; ...

return 0; }

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

(7)

7

変数とアドレスの関係

int main(void){ int a;

a = 100; ...

return 0; }

int main(void){ int a;

a = 100; ...

return 0; }

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

4 バイト

(8)

変数とアドレスの関係

int main(void){ int a;

a = 100; ...

return 0; }

int main(void){ int a;

a = 100; ...

return 0; }

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

今日のポイント1

変数 a のアドレスとは、

変数 a の置かれた先頭 の

アドレスのことである

この場合は 0x1004

(9)

9

変数のアドレスの取得方法

int main(void){ int a;

a = 100;

printf(“ 変数 a のアドレスは %p”,&a); ...

return 0; }

int main(void){ int a;

a = 100;

printf(“ 変数 a のアドレスは %p”,&a); ...

return 0; }

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

今日のポイント2

変数の前に&をつけると

変数のアドレスを取得できる

変数 a のアドレスは 0x1004 実行結果

(10)

復習:ポインタ (pointer) とは

 変数,配列,関数などの(メモリ上の)アドレスを

指し示す(参照する)

• ポインタの中身はアドレス

「~へのポインタ」

4 バイト

0x10 0

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100 9

ポインタ変数

(11)

11

ポインタの使い方 ( ポインタ変数の宣言 )

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

今日のポイント3

ポインタ変数の宣言は

型名 * ポインタ変数名 ;

(12)

ポインタの使い方(変数の宣言)

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

(13)

13

ポインタの使い方 ( アドレスの取得 )

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

今日のポイント2

変数の前に&をつけると

変数のアドレスを取得できる

(14)

ポインタの使い方 ( 値の代入 )

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

(15)

15

ポインタの使い方(ポインタ変数の出力)

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

変数 a のアドレスは 0x1008 実行結果

今日のポイント4

ポインタ変数の中身は

アドレスである

(16)

ポインタの使い方(ポインタの指す先変数の値)

0x10 0

0 0x

100

1 0x

100

2 0x

100

3 0x

100

4 0x

100

5 0x

100

6 0x

100

7 0x

100

8 0x

100

9 0x

100

a 0x

100

b 0x

100c 0x10

0

d 0x

100

e 0x

100f

変数 a のアドレスは 0x1008 変数の値は 100

実行結果

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

int main(void){

int *pa;

int a;

pa = &a;

a = 100;

printf(“ 変数 a のアドレスは %p”,pa);

printf(“ 変数 a の値は %d”,*pa);

...

return 0;

}

今日のポイント5

ポインタ変数に*をつけると ポインタ変数の指している先 の変数の値を参照できる

(17)

17

ポインタを利用して何がうれしい /できる?

 アルゴリズムとデータ構造

• リスト( 7,8 週)

木構造( 12 週)

 関数にデータを渡す( 4 週)

• 2つ以上の結果を受け取りたい場合

配列を渡したい場合

• 関数内で渡したデータを編集したい場合

 動的メモリ確保( 3 週)

関数ポインタ(上級)

コールバック

(18)

必須課題 2-1

1. p4 のプログラムを入力

2. 実行

3. 次ページの理解補助シートと出力結果を照らし合

わせて考えてみよう

4. コメントを入力

5. TA/ES に確認してもらう

(良くわからない人は1ステップ毎に、

TA/ES に確認してもらいましょう)

(19)

19

必須課題 2 - 1 :理解補助シート (1/2)

a = 10;

printf("a: %d\n", a);

printf("&a: %p\n\n", &a);

a = 10;

printf("a: %d\n", a);

printf("&a: %p\n\n", &a);

// 上の続き p = &a;

printf("p: %p\n", p); printf("*p: %d\n", *p); printf("&p: %p\n\n", &p); // 上の続き

p = &a;

printf("p: %p\n", p); printf("*p: %d\n", *p); printf("&p: %p\n\n", &p); ステップ1

ステップ2

(20)

必須課題 2 - 1 :理解補助シート (2/2)

// 前ページの続き

a = 20;

printf("a: %d\n", a);

printf("&a: %p\n\n", &a); printf("p: %p\n", p);

printf("*p: %d\n", *p); printf("&p: %p\n", &p); // 前ページの続き

a = 20;

printf("a: %d\n", a);

printf("&a: %p\n\n", &a); printf("p: %p\n", p);

printf("*p: %d\n", *p); printf("&p: %p\n", &p); ステップ3

(21)

21

復習:配列

配列

• 複数の同じ型の変数をまとめたもの

int   a[ 4 ];

int   a[ 4 ];

型 名前 要素数

(22)

文字列( char 型の配列)

char s[9] =“Ritsumei”;

char s[9] =“Ritsumei”;

文字列の決まり事

文字列の最後は

‘ \0’

“(ダブルクォーテーション):文字列を”で囲む

‘ (シングルクォーテーション):文字 1 文字を’で囲む

“(ダブルクォーテーション):文字列を”で囲む

‘ (シングルクォーテーション):文字 1 文字を’で囲む

(23)

23

文字列とポインタ

char s[9] =“Ritsumei”;

char s[9] =“Ritsumei”;

s

s は s[0] のアドレス( &s

[0] )

を示す

(s+1)

=&s[1]

(s+2)

=&s[2]

*(s+1)

=s[1]

*(s+2)

=s[2]

(24)

必須課題 2 - 2 :理解補助シート (1/4)

c = 'A'; p = &c;

printf("c: %c\n", c);

printf("&c: %p\n\n", &c); printf("p: %p\n", p);

printf("*p: %c\n\n", *p); c = 'A';

p = &c;

printf("c: %c\n", c);

printf("&c: %p\n\n", &c); printf("p: %p\n", p);

printf("*p: %c\n\n", *p);

ここの代入が終了した時点の状態を考えてみましょう ステップ1

(25)

25

必須課題 2 - 2 :理解補助シート (2/4)

// 前ページの続き

*p = 'B';

printf("c: %c\n", c);

printf("&c: %p\n\n", &c); printf("p: %p\n", p);

printf("*p: %c\n\n", *p); // 前ページの続き

*p = 'B';

printf("c: %c\n", c);

printf("&c: %p\n\n", &c); printf("p: %p\n", p);

printf("*p: %c\n\n", *p);

ここの代入が終了した時点の値とアドレスを考えてみよう ステップ2

(26)

必須課題 2 - 2 :理解補助シート (3/4)

// 前ページの続き

printf("s: %s\n", s);

printf("s[0]: %c\n", s[0]); printf("s[1]: %c\n", s[1]); printf("s: %p\n", s);

printf("&s[0]: %p\n", &s[0]); printf("*s: %c\n", *s);

printf("*(s+1): %c\n\n", *(s+1)); // 前ページの続き

printf("s: %s\n", s);

printf("s[0]: %c\n", s[0]); printf("s[1]: %c\n", s[1]); printf("s: %p\n", s);

printf("&s[0]: %p\n", &s[0]); printf("*s: %c\n", *s);

printf("*(s+1): %c\n\n", *(s+1));

出力結果と照らし合わせて、何が出力されているか考えてみよう ステップ3

char s[12] = "Ritsumeikan"; char s[12] = "Ritsumeikan";

(27)

27

必須課題 2 - 2 :理解補助シート (4/4)

// 前ページの続き

*(s+2) = 'T';

printf("s: %s\n", s); // 前ページの続き

*(s+2) = 'T';

printf("s: %s\n", s);

Tはどこに代入されるか考えてみよう ステップ4

(28)

ポインタの加算、減算

char s[9] =“Ritsumei”;

char *p = s;

p++;

char s[9] =“Ritsumei”;

char *p = s;

p++;

ここの時点

&s[0]

(29)

29

ポインタの加算、減算

char s[9] =“Ritsumei”;

char *p = s;

p++;

char s[9] =“Ritsumei”;

char *p = s;

p++;

ここの時点

&s[1]

ポインタを加算 /減算すると、ポイ

ンタ型のサイズ分(一箱分)アドレ

スがずれる

例:ポインタ変数名が p の場合

  加算: p++ または p = p+

  減算: p-- または p = p-1

(30)

必須課題 2 - 3 :理解補助シート (1/3)

char *p1, *p2;

p2 = “ Winter ”; p1 = p2; char *p1, *p2;

p2 = “ Winter ”; p1 = p2;

A1 A2 A3 A4 A5 A6 A7

p1 と p2 の値はA1~A7のどのアドレスどれでしょう?

(31)

31

必須課題 2 - 3 :理解補助シート (2/3)

// 前ページの続き

while ( *p1 != '\0' ) {

   __________________________ ; //  文字列の最後を検索 }

// 前ページの続き

while ( *p1 != '\0' ) {

   __________________________ ; //  文字列の最後を検索 }

A1 A2 A3 A4 A5 A6 A7

文字列の最後を検索するための p1 の動作を考えてみよう

(32)

必須課題 2 - 3 :チェックシート (3/3)

// 前ページの続き 

while ( _________________ ) { // アドレスを比較    __________________________ ;

   putchar( *p1 ); // アドレスを制御して文字を1つずつ出力 }

// 前ページの続き 

while ( _________________ ) { // アドレスを比較    __________________________ ;

   putchar( *p1 ); // アドレスを制御して文字を1つずつ出力 }

A1 A2 A3 A4 A5 A6 A7

p1 の動作をA1~A7を使って考えよう

p1 と p2 の関係も考えてみよう

(33)

33

まとめ(通常の変数とポインタ変数)

通常変数 ポインタ変数

宣言方法 int a; int *pa;

変数名 : a 型 : int 型

変数名 : pa

型 : int 型へのポインタ型

値の代入 a = 16; pa = &a;

変数 a の中身は (int 型の ) 数値 変数 pa の中身は( int 型変数であ る) a のアドレス

値の表示 printf("%d", a); printf("%p", pa); 整数値を表示するには %d を使

アドレスを表示するには %p を使用

演算子 読み方 意味 使用法

* (変数宣言の時) アスタリスク

(asterisk) ポインタ変数

int *pa;

* (変数宣言以外) アスタリスク ポインタが指す

先の内容

*pa = 100;

printf("%d", *pa);

& アンパサンド

(ampersand) 変数のアドレス

int a;

pa = &a;

(34)

標準入出力 (stdin, stdout)

プログラム

入力 出力

file file

stdin stdout

fp

( 自分で決めたファイルポインタ )

printf("Hello");

同じ意味

(35)

リダイレクション

35

プログラム

input.txt outp.txt

stdin stdout

stdout

stdin の入力元 , stdout の出力先を変更

• プログラムを実行するときに次のように入力

% ./a.out <input.txt >outp.txt

stdin

× ×

入力元変更 出力先変更

35

(36)

不正な参照 (1)

 segmentation fault ( セグメント例外 )

• プログラム中で使えるアドレスの範囲の外を

参照すること

• 例: int *p=NULL;

(*p)++;

• 例: int *p;

int x=0;

p=x;

NULL はアドレス 0 ( 無効なアドレス ) を示す

無効なアドレスの値に対して 演算しようとした

構文エラーではないが、 不正な参照の原因

(37)

不正な参照 (2)

 互換性のないポインタ型の代入

• char ch=‘A’;

char *q=&ch;

int *p=&ch;

構文エラーではない が、不正な参照の原

0x0a01

… …

‘A’ ch

p

0x0a02 ?????

q

0x0a01

0x0a01

q の参照先

p の参照先

(int 型は 4 バイト )

37

(38)

補足: コンパイルエラーの読み方

gcc が出力するメッセージを読むには

英語に慣れる

専門用語を知る

エラーや警告の例:

– test.c:10: error: 'y' undeclared (first use in this function) (10 行目で宣言されていない変数 y を使っている )

→ 対処例: y を宣言する、変数名の間違いを直す – test.c:5: error: redeclaration of 'x'

(5 行目ですでに宣言されている変数 x を重複して宣言した ) → 対処例: 重複している宣言の削除

– test.c:10: 警告 : 互換性のないポインタ型からの代入です

( 違う型のポインタを代入した ex. int 型ポインタに char 型ポインタを代 入 ) → 対処例: ポインタの型を修正

(39)

発展 :セグメント例外の箇所の確認方法

39

gcc –g –o prog prog.c gcc –g –o prog prog.c

gdb prog (gdb) run ...

Program received signal SIGSEGV, Segmentation fault. 0x0804837e in test ()

gdb prog (gdb) run ...

Program received signal SIGSEGV, Segmentation fault. 0x0804837e in test ()

セグメント例外が起きた箇所の 関数名がわかる

(40)

著者リスト

1. 安積 卓也(情報システム学科)

2. 大森 隆行(情報システム学科)

参照

関連したドキュメント

卒論の 使用言語 選考要件