モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

39
モモモモモモモモモモモモ モモモモモモモモモモモ

Upload: sulwyn

Post on 09-Jan-2016

52 views

Category:

Documents


6 download

DESCRIPTION

モバイルプログラミング第3回 Cプログラミングの基礎( 2 ). 前回まで: microshell. #include char command[80]; char *prompt="% "; main() { printf("%s", prompt); while(scanf("%s", command) != EOF){ if(fork() == 0){ execl(command, command,0); }else{ wait(0); - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

モバイルプログラミング第3回

Cプログラミングの基礎( 2 )

Page 2: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

前回まで: microshell#include <stdio.h> char command[80];char *prompt="% ";

main(){ printf("%s", prompt); while(scanf("%s", command) != EOF){ if(fork() == 0){ execl(command, command,0); }else{ wait(0); } printf("%s", prompt); }}

Page 3: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

前回の課題• 課題 1. ls コマンドを呼び出して実行できるようにしよう。

Hint. %/bin/ls と実行すれば実行できますが、 %ls だけでは実行できません。 パスをサーチするには ? execl execlp⇒

• 課題 2. 知らないコマンドがでたらエラーを返すようにしよう。Hint. 大抵のシステムコール・関数は正常に実行できたかどうか確認するため返り値という値を返します。if( ( execlp(command, command, 0) ) <0 )

printf(“error\n”); 等

• 課題 3. ls -l や cat コマンドのように、第 2 引数を処理するようにしよう。第2引数を char * argv[] 等に入れてexecvp(argv[0], argv) のようにする

Page 4: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

#include <stdio.h>

char *prompt="% "; char *argv[20], input[80];

main(){ printf("%s", prompt); while( fgets(input, sizeof(input), stdin) != NULL ){ if(fork() == 0){

/* input を空白で分割し “ ls -l” “ls” “-l” argv⇒ に格納する */

if(   execvp(argv[0], argv)<0   ) perror(argv[0]);

}else{wait(0);

} printf("%s", prompt); }}

解答例

Page 5: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

傾向• 第3課題でつまるケースが多かった

char * argv[] を整える方法

• 応用では cd やパイプを実装するケースが多かった

Page 6: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

今日やること 関数 ポインタ 配列 文字列

演習はこれらを応用した文字列分割とヒストリー機能

Page 7: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

関数とは

頻繁に使うコードをまとめたもの

int func(int a, char b){

}

返り値(戻り値)の型

出力

関数名 引数の型と名前

  入力

Page 8: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

関数の定義int add(int a, int b){

int num;

num = a+b;

return num;

}

関数は return で正しい型の返り値を返さないといけない

呼び出し側が得られる唯一の結果

Page 9: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

関数の宣言と呼び出し• int 型以外の返り値を持つ関数は、変数同

様宣言する必要がある

void func(int a, char b);

main(){

func(x, y);

}

void func(int a, char b){ … }

定義は別の場所でできる

宣言

宣言が無いと implicit( 暗黙に )int を返すと解釈する

Page 10: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

返り値• int 型の返り値は定義で省略しても良い

main(){  … }  等

• 返り値は呼び出し側が無視しても OK

単に execvp(…); 等と呼び出して良い

(int が返ってるけど )

Page 11: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

引数

int func(int a, int b){

}

main(){

func(x, y);

}

    a, b 仮引数

(呼び出され側)

x, y 実引数(呼び出し側)

どんな値が渡されるか分からないから 「仮」

関数への入力

Page 12: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

関数への引数の渡し方

• 値渡し  call-by-value• ポインタ ( アドレス ) 渡し

関数

① ②

関数

値ポインタ複製

func(int a){…} func(int * a){…}

Page 13: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

これまでに利用した関数From jman page• int execlp(const char *file, const char *arg,

...); • int execvp(const char *file, char *const argv

[]); • int scanf(const char *format, ...); • char *strcpy(char *dest, const char *src);

…( ピリオド三つ ) は可変個の引数を表すconst はその変数の値が変更されないことを保証する

ポインタ

Page 14: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

ポインタ

6

0 番地

1 番地

2 番地

3 番地

メモリ

値 変数

a

26

変数が入っているメモリ番地を表す変数

ポインタ値

int a;

int * a_ptr;

Page 15: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

*と&• 宣言

int * a_ptr;

• 使い方

a_ptr  = & a;  * a_ptr  = 6 ;

&でアドレスを取得

*はポインタが指している中身を表す

宣言内の*は変数がポインタであることを表す

Page 16: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

ポインタと中身の関係

&a

int a;

int * b;

代入できるb

* b 代入できる a

a代入不可b

&a* b 代入不可

Page 17: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

ポインタと引数

 値渡し  call-by-value ポインタ ( アドレス ) 渡し

① ②func1(int a){

return a++;

}

func2(int * a){

a = “abc”;

}

実際に a は渡さない

実際に a を処理できる

Page 18: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

具体例main(){

int a, b;int *a_ptr;

b = func1(a);

func2(a_ptr)}

a の複製が渡され、値が返る

a_ptr の指す中身が直接処理される、配列などでよく使われる

Page 19: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

それぞれの違い• 値渡し

長所: 渡した値に何されても平気短所: 適切な値を return し、処理しないとい

けない

• ポインタ渡し長所: 関数内で全て処理が行われるから便利

   配列を渡す場合は必然短所: 変数の中身が変更される可能性がある

Page 20: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

ポインタの型• ポインタは型を持つ

a_ptr は特定の型の変数を指し* a_ptr はその型の中身を持つ

Int, char, long… それぞれのポインタは異なる

Page 21: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

ポインタの出力• printf(“%x”, ptr);

16 進数 (Hexadecimal)例: 22f064

• printf(“%p”, ptr);アドレスを出力出力例: 0x22f064

Page 22: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

ポインタの例main(){ int num = 3; int * num_ptr;

num_ptr = &num; * num_ptr = 6; printf("%d\n", num); printf(“%d\n”, * num_ptr); printf("%x\n", num_ptr);}

出力結果

$./a.exe6622f06c$

num のアドレスを num_ptr に代入

num_ptr が指す値( =num )

ポインタとして宣言

Page 23: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

ポインタの初期化int * a_ptr = &a;   // アドレスの取得int a[10];

Int * a_ptr = a;   // 配列の名前Int * a_ptr = &a[2];   // 要素のアドレス

初期化していないポインタを使うと多くの場合エラーになる

Page 24: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

配列

char a[3];int num[20];int num[10][10]…;

a = { x, y, z };num = { {1, 2, 3, …, 10} ,

{1,2, 3, …, 10} }

x

y

z

……

複数の要素を持つ変数

a[0]a[1]

a[2]

Page 25: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

配列の名前とポインタ

• 実際は最初の要素を指すポインタ• int * a[10]  なら a == &a[0]

配列の名前の実体はポインタ

a a[0]

Page 26: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

関数への受け渡し• 配列として受け渡せない• 配列名(ポインタ)を渡すInt buf[20];

func(buf);

int func(int * num){

}要素数などを渡す手段が必要

文字列の場合終端記号 \0 で終わりを判別

char a[3];

char b[3];

strcat(a, b);

Page 27: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

execl での例int execl(const char *path, const char *arg, ...);

char command[80];

main(){

…execl(command, command,0);…

配列の名前なので渡せている

Page 28: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

多次元配列とポインタの配列• 多次元配列: num[2][3]

• ポインタの配列: char * argv[6]

全ての要素がポインタ

多次元配列

配列

配列

Page 29: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

それぞれ関数への受け渡しポインタが渡るように注意するexecvp などはポインタの配列を引数に取るint execvp(const char *file, char *const argv[]);

sum(num[1]);int sum(int * num){

for(;;)total += num[i];return total;

}

execvp(argv[o], argv);

Page 30: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

配列とポインタへの演算

a_ptr

a_ptr+1

a_ptr-1

ポインタには加算と減算ができる

値を出すときは*( a_ptr+1 )

* a_ptr+1 だと中身に1が加わってしまう

Page 31: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

加算の例文字列の中から特定の文字を探す

char buf[80];

char * ptr = buf;

while( * (ptr++)!=‘ 文字’ )

;

ptr が文字を指した状態でループを抜ける

ポインタの初期化

値を取り出し比較

Page 32: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

加算の例2配列の x 番目の要素は名前+ x

char a[6];

char * ptr = a

a[0]

a[1]

a[2]

a[3]a[4]

a[5]a+4

Page 33: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

文字列

char の配列として扱われる

1. 連続した文字の並び2. ヌル文字( \0 )で終わる

a b c \0例

Page 34: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

宣言

char * buf = “abc”;

a b c \0

buf はこの要素へのポインタ

Page 35: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

‘  ’ と “ ” の違い

• “a” は 01100001 00000000 に自動変換される

• ‘a’ は 01100001

• 終端記号 ‘ \0’ は 8 つの 0

Page 36: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

文字列の検査char * buf   = “abc\0xyz”;

char * ptr = buf, * ptr2;

While( *( ptr++ ) )

;

ptr2 = ptr;

printf(“%s”, ptr2);

出力結果は xyz になる

‘\0’ は偽なので、文字列の終端で自動的にwhile を抜ける

Page 37: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

strlen と sizeof

• strlen: 最初の \0 までの長さ• sizeof :オブジェクトの大きさを求めるのに使

a b c \0 x y z \0char * buf

strlen(buf) == 3

sizeof(buf) == 8

Page 38: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

文字列の取得char *fgets(char *s, int size, FILE *stream);

char buf[80];

fgets(buf, sizeof(buf), stdin);

buf[strlen(buf) -1] = '\0';改行文字も読んでしまうのでその前で終端させる

a b c \n \0buf

Page 39: モバイルプログラミング第3回 Cプログラミングの基礎( 2 )

今日の演習• 課題1 一行の入力を空白で分割し、 char *argv[n] に格納する関

数 split( 名前はなんでも良い ) をつくろう。ただし、ポインタと文字列 ( 終端記号 ) の性質を利用すること。

• 課題2 実行したコマンドを配列に保存し、特定のキーを押したら過去のコマンドのリストを表示、実行できる機能 ( ヒストリー ) を microshell に付加しよう。

% h0) ls1) ls -l 2) gcc mshell.c% 0a.exe mshell.c%