Download presentation
Presentation is loading. Please wait.
1
モバイルプログラミング第3回 Cプログラミングの基礎( 2 )
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); }
3
前回の課題 課題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)のようにする
4
解答例 #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); }
5
傾向 第3課題でつまるケースが多かった char *argv[]を整える方法 応用ではcdやパイプを実装するケースが多かった
6
今日やること 関数 ポインタ 配列 文字列 演習はこれらを応用した文字列分割と ヒストリー機能
7
関数とは 頻繁に使うコードをまとめたもの int func(int a, char b){ … } 返り値(戻り値)の型 出力 関数名
引数の型と名前 入力 int func(int a, char b){ … }
8
関数の定義 int add(int a, int b){ int num; num = a+b; return num; }
返り値を返さないといけない 呼び出し側が得られる 唯一の結果
9
関数の宣言と呼び出し void func(int a, char b); main(){ func(x, y); }
宣言が無いとimplicit(暗黙に)intを返すと解釈する 定義は別の場所でできる
10
返り値 int型の返り値は定義で省略しても良い 返り値は呼び出し側が無視してもOK main(){ … } 等
単にexecvp(…); 等と呼び出して良い (intが返ってるけど)
11
引数 関数への入力 main(){ func(x, y); } x, y 実引数(呼び出し側)
int func(int a, int b){ … } a, b 仮引数 (呼び出され側) どんな値が渡されるか分からないから 「仮」
12
関数への引数の渡し方 値渡し call-by-value ポインタ(アドレス)渡し ① ② 値 ポインタ 値 値 関数 関数
func(int a){…} ② func(int *a){…} 複製 値 ポインタ 値 値 関数 関数
13
これまでに利用した関数 ポインタ 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はその変数の値が変更されないことを 保証する ポインタ
14
ポインタ 2 6 変数が入っているメモリ番地を表す変数 値 ポインタ 6 a int a; int *a_ptr; … メモリ 値 変数
0番地 1番地 6 a 2番地 int a; int *a_ptr; 3番地 …
15
*と& int *a_ptr; a_ptr = &a; *a_ptr = 6; &でアドレスを取得 宣言 宣言内の*は変数が
使い方 a_ptr = &a; *a_ptr = 6; 宣言内の*は変数が ポインタであることを表す &でアドレスを取得 *はポインタが 指している中身を表す
16
ポインタと中身の関係 int a; int *b; 代入不可 b a &a b 代入できる *b 代入できる a *b 代入不可 &a
17
ポインタと引数 値渡し call-by-value ポインタ(アドレス)渡し ② ① 実際にaは渡さない 実際にaを処理できる
ポインタ(アドレス)渡し ② ① func1(int a){ return a++; } func2(int *a){ a = “abc”; } 実際にaは渡さない 実際にaを処理できる
18
具体例 main(){ int a, b; int *a_ptr; b = func1(a); func2(a_ptr) }
19
それぞれの違い 値渡し ポインタ渡し 長所: 渡した値に何されても平気 短所: 適切な値をreturnし、処理しないといけない
長所: 渡した値に何されても平気 短所: 適切な値をreturnし、処理しないといけない ポインタ渡し 長所: 関数内で全て処理が行われるから便利 配列を渡す場合は必然 短所: 変数の中身が変更される可能性がある
20
ポインタの型 a_ptrは特定の型の変数を指し *a_ptrはその型の中身を持つ ポインタは型を持つ
Int, char, long…それぞれのポインタは異なる
21
ポインタの出力 printf(“%x”, ptr); 16進数(Hexadecimal) 例:22f064
printf(“%p”, ptr); アドレスを出力 出力例:0x22f064
22
ポインタの例 ポインタとして宣言 numのアドレスをnum_ptrに代入 出力結果 num_ptrが指す値(=num) $./a.exe 6
main() { int num = 3; int *num_ptr; num_ptr = # *num_ptr = 6; printf("%d\n", num); printf(“%d\n”, *num_ptr); printf("%x\n", num_ptr); } numのアドレスをnum_ptrに代入 出力結果 $./a.exe 6 22f06c $ num_ptrが指す値(=num)
23
ポインタの初期化 int *a_ptr = &a; // アドレスの取得 int a[10];
初期化していないポインタを使うと多くの場合エラーになる
24
配列 … a[0] x a[1] y 複数の要素を持つ変数 a[2] z char a[3]; int num[20];
a = { x, y, z }; num = { {1, 2, 3, …, 10} , {1,2, 3, …, 10} } …
25
配列の名前とポインタ 配列の名前の実体はポインタ a[0] a 実際は最初の要素を指すポインタ
int *a[10] なら a == &a[0] a[0] a
26
関数への受け渡し 配列として受け渡せない 配列名(ポインタ)を渡す Int buf[20]; func(buf);
int func(int *num){ } char a[3]; char b[3]; strcat(a, b); 要素数などを渡す手段が必要 文字列の場合終端記号\0で終わりを判別
27
execlでの例 char command[80]; main() { … execl(command, command,0);
int execl(const char *path, const char *arg, ...); char command[80]; main() { … execl(command, command,0); 配列の名前なので渡せている
28
多次元配列とポインタの配列 配列 多次元 配列 配列 多次元配列: num[2][3] ポインタの配列: char *argv[6]
全ての要素がポインタ 配列
29
それぞれ関数への受け渡し execvp(argv[o], argv); sum(num[1]); int sum(int *num){
ポインタが渡るように注意する execvpなどはポインタの配列を引数に取る int execvp(const char *file, char *const argv[]); execvp(argv[o], argv); sum(num[1]); int sum(int *num){ for(;;) total += num[i]; return total; }
30
配列とポインタへの演算 ポインタには加算と減算ができる a_ptr-1 a_ptr a_ptr+1 値を出すときは*(a_ptr+1)
31
加算の例 文字列の中から特定の文字を探す char buf[80]; char *ptr = buf;
while(*(ptr++)!=‘文字’) ; ptrが文字を指した状態でループを抜ける ポインタの初期化 値を取り出し比較
32
加算の例2 a[0] char a[6]; char *ptr = a a[1] a[2] a[3] a[4] a+4 a[5]
配列のx番目の要素は名前+x a[0] char a[6]; char *ptr = a a[1] a[2] a[3] a[4] a+4 a[5]
33
文字列 連続した文字の並び ヌル文字(\0)で終わる 例 a b c \0 charの配列として扱われる
34
宣言 char *buf = “abc”; a b c \0 bufはこの要素へのポインタ
35
‘ ’ と “ ” の違い “a”は 01100001 00000000に自動変換される ‘a’は 01100001
‘ ’ と “ ” の違い “a”は に自動変換される ‘a’は 終端記号 ‘\0’ は8つの0
36
文字列の検査 ‘\0’は偽なので、文字列の終端で自動的にwhileを抜ける char *buf = “abc\0xyz”;
char *ptr = buf, *ptr2; While(*(ptr++) ) ; ptr2 = ptr; printf(“%s”, ptr2); 出力結果はxyzになる ‘\0’は偽なので、文字列の終端で自動的にwhileを抜ける
37
strlenとsizeof strlen: 最初の\0までの長さ sizeof:オブジェクトの大きさを求めるのに使う char *buf a
x y z \0 strlen(buf) == 3 sizeof(buf) == 8
38
文字列の取得 buf a b c \n \0 char *fgets(char *s, int size, FILE *stream);
char buf[80]; … fgets(buf, sizeof(buf), stdin); buf[strlen(buf) -1] = '\0'; 改行文字も読んでしまうのでその前で終端させる buf a b c \n \0
39
今日の演習 課題1 一行の入力を空白で分割し、char *argv[n]に格納する関数split(名前はなんでも良い)をつくろう。
ただし、ポインタと文字列(終端記号)の性質を利用すること。 課題2 実行したコマンドを配列に保存し、特定のキーを押したら過去のコマンドのリストを表示、 実行できる機能(ヒストリー)をmicroshellに付加しよう。 % h 0) ls 1) ls -l 2) gcc mshell.c % 0 a.exe mshell.c %
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.