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

42
モモモモモモモモモモモモ モモモモモモモモモモモ

Upload: amaris

Post on 09-Jan-2016

39 views

Category:

Documents


5 download

DESCRIPTION

モバイルプログラミング第4回 Cプログラミングの基礎( 3 ). 前回の復習. ポインタ 配列 文字列. ポインタ. 変数が入っているメモリ番地(アドレス)を表す変数. 値. ptr. 2. 0 番地. 6. 1 番地. ptr. 6. 2 番地. printf( “ ptr: %x ” , ptr); ptr: 2. 3 番地. …. ポインタの使い方. 宣言内の*は変数が ポインタであることを表す ポインタは型を持っていて その型の中身を持つ. void func(int * a); main(){ int * a_ptr; - PowerPoint PPT Presentation

TRANSCRIPT

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

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

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

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

前回の復習

• ポインタ• 配列• 文字列

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

ポインタ

6

0 番地

1 番地

2 番地

3 番地

ptr

26

変数が入っているメモリ番地(アドレス)を表す変数

値 ptr

printf(“ptr: %x”, ptr);

ptr: 2

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

ポインタの使い方

void func(int * a);main(){ int * a_ptr;

a_ptr  = & a;  * a_ptr  = 6 ;}

&でアドレスを取得

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

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

その型の中身を持つ

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

main()

{

int num = 3;

int * num_ptr;

num_ptr = #

* num_ptr = 6;

printf("%d\n", num);

}

出力結果

$./a.exe6

num のアドレスを num_ptr に代入

ポインタとして宣言

記述例

num_ptr の中身に値を代入

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

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

関数

① ②

関数

値ポインタ複製

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

文字列や配列を関数に渡すために必要

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

配列

• char a[80];

• int num[20];

• int num[10][10];

// 配列の配列(二次元配列)

• char * argv[10];

// ポインタの配列

x

y

z

複数の要素を持つ変数

a[0]a[1]

a[2]

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

配列とポインタ

• 配列の名前はポインタ

• ポインタに加算すると次の要素、減算すると前の要素を指すようになる

ptr

ptr+3

char buf[4];char * ptr = buf;

buf bufptr+1ptr++

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

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

a b c \0例

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

文字列の宣言

char * buf = “abc”;

a b c \0

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

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

前回の課題• 課題 1. 文字列を空白で分割する関数をつくろ

う。ただし、ポインタと文字列 ( 終端記号 ) の性質を利用すること。

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

http://www.ht.sfc.keio.ac.jp/mobile2004/lecture4.htmにサンプルコード

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

int split();

main()

{

if(fork() == 0){

split(input, argv); // 入力を空白で分割し、 argv に格納する

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

perror(argv[0]);

}

}

int split(char *input, char *argv[]){

int i;

argv[0] = input; // 最初に input の先頭を argv[0] に入れておく

while(*(input++)) // input の今の文字が終端 (\0 、つまり偽 ) で無い限り if(*input==' '){ // input の今の文字が空白なら *input = '\0'; // 代わりに終端文字を入れる argv[++i] = input+1; // i を増やして、 argv[i] に input の次の文字列を入れる }

return i;

}

課題1 解答例

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

int split(char *input, char *argv[]){

int i;

argv[0] = input; // 最初に input の先頭を argv[0] に入れておく

while(*(input++)) // input の今の文字が終端 (\0 、つまり偽 ) で無い限り if(*input==' '){ // input の今の文字が空白なら *input = '\0'; // 代わりに終端文字を入れる argv[++i] = input+1; // argv の次のポインタに input の次の文字列を入

れる }

return i;

}

split の内容

\0l-sl

文字列を見ていって

空白に \0 を入れてポインタで次の文

字を指す

\0

argv[0] argv[1]

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

char history[20][80];int history_num = 0;

void process_history(char *input);

main(){ … while(fgets(input, sizeof(input), stdin)){ … if(input[0] == 'h'){

process_history(input); } else{

if(history_num < 20) strcpy(history[history_num++], input);  

}if(fork() == 0)...

}

void process_history(char *input){ int i, j; char buf[80];

for(i=0; i<history_num; i++) printf("%d) %s \n", i, history[i]); printf("input: "); fgets(buf, sizeof(input), stdin); j = atoi(buf); strcpy(input, history[j]); }

課題2 解答例

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

呼び出し側char history[20][80];if(!strcmp(input, “h”)){

process_history(input);     // 入力が h なら、ヒストリーから選択させ、 input にコマンドを代入 } else{ if(history_num < 20)

strcpy(history[history_num++], input); //  そうでなければ history に追加}… input の実行関数void process_history(char *input){ int i, j; char buf[80]; for(i=0; i<history_num; i++) printf(“%d) %s \n”, i, history[i]); // history を表示 printf(“input: ”); fgets(buf, sizeof(input), stdin); // ユーザの入力を取る j = atoi(buf); // それを数字に直して strcpy(input, history[j]); // history に代入}

process_history の内容

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

今日やること講義main への引数ファイル構造体プリプロセッサ

演習リダイレクション機能

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

main への引数

main(int argc, char *argv[]) と宣言することでmain 自体が引数を取れる

main(int argc, char * argv[]){

int i=argc;

char *cmd=argv[0]

}

main(){

fgets(…);

}

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

argc, argv

• どちらも自動的に計算されて main に渡される

argc  = コマンドラインからの引数の数argv  = 引数を表すポインタ配列argv[0] はプログラム名、 argv[1] は第一引数…

main(int argc, char * argv[])

$./a hello world

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

記述例#include <stdio.h>int i;

main(int argc, char *argv[]){ printf("argc = %d\n", argc);

for(i=0; i<argc; i++) printf("argv[%d] = %d\n", i, argv[i]);}

実行結果

$gcc sample.c$./a.exe hello worldargc = 3argv[0] = ./aargv[1] = helloargv[2] = world$

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

練習問題

コマンドラインから引数として二つ文字列を取ってそれらをつなげるプログラムを作ろう

引数の数が異なったらエラーメッセージを出そう

int strncmp(const char *s1, const char *s2, size_t n); 二つの文字列を比べる

char *strncpy(char *dest, const char *src, size_t n); 文字列をコピーする

char *strncat(char *dest, const char *src, size_t n); 二つの文字列を連結する

n にはstrlen を使うと良い

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

nm コマンド

• nm を使って a.exe を見る

   T テキスト D データ BSS 未初期化データ

$nm a.exe00000000 A __dll__00401050 T _function00401055 T _main00402000 D __data_start__00402000 D _a00402004 D _b00402010 d _dw2_object_mutex.000402014 d _sjl_once.200402020 D __data_end__00403000 b .bss00403000 B __bss_start__00403010 b _sjl_fc_key.100403020 B _environ00403024 B __impure_ptr00403028 B __fmode00404114 i .idata$6

addr 低

addr 高

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

プログラムの中身を見る

アセンブラコード

$ gcc –S ファイル .c$ less ファイル .s

gdb

$ gdb a.exe…(gdb) disassemble main

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

ファイル

• ファイル型変数  FILE *(ファイルポインター)で扱える

• ファイルを扱うプログラムはstdio.h を include する必要がある

バイトの羅列ディスク上のデータ、端末などをファイルとして扱う

ファイル

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

ファイル関連の関数

オープン・クローズFILE *fopen(const char *path, const char

*mode); int fclose(FILE *stream);

読み書きchar *fgets(char *s, int size, FILE *stream); int fputs(const char *s, FILE *stream);

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

fopen のモード

モード(これらを組み合わせて指定する)

r  読み込みr+  書き込みもできるw  書き込み、ファイルの内容を初期化w+  読み込みもできるa  追加書き込みa+  読み込みもできるb  バイナリファイルとしてオープンする

詳しくは jman の fopen を参照

FILE *fopen(char * ファイル名 , const char * モード );

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

記述例main(){ FILE *file; char buf[80]; file = fopen(“sample2.c", "r");

fgets(buf, sizeof(buf), file); fputs(buf, file); printf("%s", buf);

fclose(file);}

読み込み専用で開いている

書き込みは行われない

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

定義済みの名前

それぞれ FILE * 型変数プログラムを実行すると自動的に開かれる

stdin  標準入力(キーボード)stdout  標準出力(スクリーン)stderr  標準エラー出力(スクリーン)

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

ファイル記述子

• ファイルディスクリプターとも呼ぶ• FILE * 型の中にも含まれる• stdin 0, stdout 1, stderr 2

int open(const char *pathname, int flags); int creat(const char *pathname, mode_t mode); int close(int fd);

ファイルポインターやアドレスの代わりにやり取りされる整数値

オープンされているファイルのテーブルに対するインデックス

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

dup 関数

int dup(int oldfd);  使用されていない最小の値のディスクリプタ

を新しいディスクリプタとして使用する

int dup2(int oldfd, int newfd);   oldfd の複製として newfd を作成する。必要

ならば最初に newfd をクローズ (close) する

ファイル記述子の複製を作る

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

#include <stdio.h>char buf[80];main(int argc, char *argv[]){ FILE *file = fopen("tmp", "w"); dup2(file->_file, 1);

if(argc==3){ strncpy(buf, argv[1], strlen(argv[1])); strncat(buf, argv[2], strlen(argv[2])); printf("%s",buf); } else printf("usage: a string1 string2"); fclose(file);}

標準出力の1を閉じてtmp の fd に割り当てる

printf の内容はtmp に書き込まれる

記述例

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

pipe 関数

• パイプ: 一つのプロセスの出力をもう一方の入力にする (例) ls | less

int pipe(int filedes[2]);   filedes[0] には読み出し用、 filedes[1] には書き込

み用のファイル・ディスクリプターが格納される

パイプ用のファイル記述子の組を作る

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

構造体

複数の異なる要素を持った変数

配列は各要素が同じデータ型だったが、構造体は各要素のデータ型が異なる

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

構造体の宣言

構造体自体struct NAME{

int one;char two;struct hoge three;

} [ 変数名 ];

変数struct NAME a, b, c[10];

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

FILE の構造体struct __sFILE { unsigned char *_p; /* バッファ内の現在のポジション */ int _r; /* getc() 用に残っているスペース */ int _w; /* putc() 用に残っているスペース */ short _flags; /* フラグ、0ならフリー */ short _file; /* ファイル記述子 */ struct __sbuf _bf; /* バッファ */ …}

/usr/include/sys/reent.h に定義されている

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

要素の参照

.  要素の実体を参照する->  要素のポインタを参照する

例)a.one = 3;

b.two = ‘x’;

c->one = &(a.one)

dup(file->_file, 1);

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

typedef

データ型に別の名前を付ける

typedef int suuji;typedef struct{

int a;char b;…

} hoge;typedef __FILE FILE;

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

プリプロセッサ- ヘッダーファイルを読むために  -

#include<stdio.h>#define HOGE 100  などヘッダーファイルを読み込んだり、変数

やマクロが展開されたりする単純な置き換え

プログラムがコンパイルされる前に行われる前処理

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

プリプロセッサコマンドの書き方

#  で始める¥ で複数行に渡って書く; は要らない

例)#include<string.h>#define SIZE 100

for(i=0; i<SIZE; i++); ←SIZE が 100 に置き換わる

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

マクロ

#define kakeru(a, b) ((a) * (b))

int x = kakeru(2, 3);

と書くと、コンパイルする前に

int x = ((2) * (3));

と置き換えられる

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

#ifdef

• 変数が #define されていたらコードを有効にする例)#define DEBUG 1 ←0 なら DEBUG しない…

#ifdef DEBUG

printf(“input = %s”, input);

#endif

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

ヘッダーファイル

• 通常 名前 .h

• 関数プロトタイプやマクロ定義が含まれている

ヘッダーファイルの定義もコンパイル前に展開される

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

今日の演習

• コマンドラインから引数として複数のファイル名を取り、その内容をつなげるプログラムを作ろう

• Microshell を拡張し、リダイレクションを使

えるようにしよう

リダイレクション:コマンドの後ろに > をつけることで、コマンドの実行結果を stdoutではなく、ファイルに書き込む