システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/syspro-2004/2004-04-19
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
Java では、char は、整数型とはまったく違う。相互に代入できない。 (Java の char は、内部的には、16 ビット。Unicodeで符号化される。)
int i ; char c; c = i ; /* C では OK. Java ではエラー */
図? バイト・オーダ(その1) 図? バイト・オーダ(その2)
1: /* 2: byte-order.c -- バイト・オーダを調べるプログラム 3: ~yas/syspro/cc/byte-order.c 4: Start: 2001/04/22 19:01:41 5: */ 6: 7: int i=0x12345678 ; 8: 9: main() 10: { 11: char *p,c ; 12: printf("&i == 0x%x\n",&i ); 13: p = (char *)&i ; 14: printf(" p == 0x%x\n",p ); 15: c = *p ; 16: printf(" c == 0x%02x\n",c ); 17: printf("{0x%02x,0x%02x,0x%02x,0x%02x} \n",p[0],p[1],p[2],p[3] ); 18: }実行結果。デバッガ gdb で変数の番地を調べて、その番地の内容を x コマン ドで表示する。
% cc -g byte-order.c -o byte-order% ./byte-order
&i == 0x80495f0 p == 0x80495f0 c == 0x78 {0x78,0x56,0x34,0x12} % gdb byte-order
GNU gdb Red Hat Linux (5.1-0.71) Copyright 2001 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb) r
Starting program: /home/lab/Denjo/yas/syspro-2002/cc/byte-order &i == 0x80495f0 p == 0x80495f0 c == 0x78 {0x78,0x56,0x34,0x12} Program exited with code 027. (gdb) p &i
$1 = (int *) 0x80495f0 (gdb) x 0x80495f0
0x80495f0 <i>: 0x12345678 (gdb) x/4b 0x80495f0
0x80495f0 <i>: 0x78 0x56 0x34 0x12 (gdb) q
%
![]()
main() { char c = 'A' ; f( c ); /* ここで符合拡張が行われる */ } f(char c) /*int c でも受けられる。*/ { .... }
ssize_t write(int fd, const void *buf, size_t count); ssize_t read(int fd, void *buf, size_t count);次のプログラムは、間違いである。
main() { void *buf ; read( 0, buf, 1 ); }ポインタ buf 変数は、番地を保持する。ポインタを使う時には、どの番地を 指しているかが大事である。初期化されていない。ポインタも、どこかの番地 を差している。そのどこかは、メモリが割り当てられていないかもしれない。
ポインタは、必ず次のいずれかの方法で初期化して使う。
int x ;
buf = &x ;
浮動小数点以外の小数の符合化の方法も考えられる。
キーボードで打てるのは、ASCII 文字の並びである。キーボードから int を 読みたい時には、文字の並びとしてまず読み込み(fgets())、次に、 文字の並びから整数に変換する(atoi(), strtol(), sscanf())
scanf() は、人間が打つ、間違い(意図的な攻撃)を含む可能性がある場所で 使うべきではない。
OSの中には、キーボードからASCII 文字の並び('0' から'9')を打ち込むと、OSの中で整に変換する機能が あるものもある。Unix には、そのような機能はない。文字の並びではなく数 が必要ならば、ライブラリ関数で変換する。
バイトオーダを変換するには、次のような関数を使う。
YHM_Esape(#includeここで、n は、network、h は、ホスト、s は short、l は、long を意味する。 ネットワークの変りに、ファイルでもよい。ファイルやネットワークに出力す る時には、htonl() や htons() で標準系(ビッグエンディアン)に変換する。 入力の時は、逆に ntohl() や ntohs() で元にもどす。) unsigned long int htonl(unsigned long int hostlong ); unsigned short int htons(unsigned short int hostshort); unsigned long int ntohl(unsigned long int netlong ); unsigned short int ntohs(unsigned short int netshort );
A 1 B 2 C 3 ... Z 26モールス符合。
ポケットベル・コード。
コンピュータでよく使われる文字集合と文字の符号化の方法
数の値と数を画面に文字として表示するために使う符合が違うことに注意する。 数の0と文字の0は違う。
8ビット符号。 20(16進)から7F(16進)までは、ASCII と同じ。 西ヨーロッパでよく使われている。
参考:
http://www.asahi-net.or.jp/~sd5a-ucd/rec-html401j/sgml/entities.html,http://www.asahi-net.or.jp/~sd5a-ucd/rec-html401j/sgml/entities.html
漢字1文字を、7ビットと7ビットの組でで表す。それぞれ 21〜7E に入るよ うになっている。16進で2121から747Eまでの範囲にある。ただし、半分は空い ている。
参考:
http://www.hlla.is.tsukuba.ac.jp/~yas/classes/ipe/nitiniti2-enshu-1996/1996-11-18/kanji-code.html
int x ; x = 'A' ; /* (1) */ x = "A" ; /* (2) */
char の配列として表現される。連続番地に置かれ、0終端されている。ダブ ルクォート「""」で囲むと、名前がない配列が作られ、その先頭番地が入る。
C言語のプログラムの中に 漢字を「""」で囲んだ時には、EUC だと比較的そのまま通る。 JIS だと、「"」や「\」と重なるバイトを持つ漢字が現れた時に問題が生じる。
ファイルから読み込む時には、0終端という約束に従っていれば、どんな 文字コードでも扱えるとも言える。
C言語で "" で括った文字列を指定すると、名前がない charの配列が作られ、 その先頭番地が返される。配列の内容は、指定された文字と、最後に文字列の 終端を意味する 0 が付く。表面的に見える文字の数より 1 バイト多いことに 注意する。
1: /* 2: string-array.c -- 文字列と配列 3: ~yas/syspro/string/string-array.c 4: Start: 2002/04/21 18:24:34 5: */ 6: 7: char h1[] = { 'h','e','l','l','o',0 }; 8: 9: main() 10: { 11: int i ; 12: printf("%s\n",h1 ); 13: printf("0x%x, 0x%x\n",h1,"hello" ); 14: for( i=0 ; i<5 ; i++ ) 15: { 16: printf("[%c]", h1[i] ); 17: } 18: printf("\n"); 19: for( i=0 ; i<5 ; i++ ) 20: { 21: printf("[%c]", "hello"[i] ); 22: } 23: printf("\n"); 24: }
配列なので、"hello"[i] のようなこともできる。
printf() の %x, %s, %c の意味に注意する。
%x
%s
%c
実行結果。
% cp ~yas/syspro/string/string-array.c .% make string-array
cc string-array.c -o string-array % ./string-array
hello 0x80495b8, 0x804858c [h][e][l][l][o] [h][e][l][l][o] %
![]()
#include <string.h> 文字列の長さを調べる(終端の 0 は、長さに含まれない) size_t strlen(const char *s); 文字列のコピー char *strcpy(char *dest, const char *src); // 使わない方がよい char *strncpy(char *dest, const char *src, size_t n); // 使わない方がよい char *strdup(const char *s); 文字列の連結 char *strcat(char *dest, const char *src); // 使わない方がよい char *strncat(char *dest, const char *src, size_t n); // 使わない方がよい 文字列の比較 int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n);str"n"cpy() は、n バイトだけコピーする。n より短かったら、残りの部分に 0 を埋める。str"n"cat() も同様である。固定長の構造体の内部の文字列を初 期化する場合には使えるが、それ以外にはあまり使い道がない。
str"case"cmp では、大文字(upper case)と小文字(lower case)の区別をしな い。
strcpy() や strcat() は、バッファ・オーバーフローが起きないように使う には難しい。OpenBSD, FreeBSD, SunOS 5.8 には、より優れた API がある。
size_t strlcat(char *dst, char *src, size_t dstsize); size_t strlcpy(char *dst, char *src, size_t dstsize);strlcat() は、strncat() と似ているが、最後に 0 を埋めることはない。
strcpy(), strcat() の代りに次の関数も便利である。
#include <stdio.h> int sprintf(char *str, const char *, ...); int snprintf(char *str, size_t size, const char *,...); #include <stdag.h> int vsprintf(char *str, const char *, va_list ap); int vsnprintfvsnprintf(char *str, size_t size, const char *, va_list ap);その他
文字列中の文字や文字列の検索 char *strchr(const char *s, int c); char *strrchr(const char *s, int c); char *strstr(const char *haystack, const char *needle); char *index(constchar*"s, int c); char *rindex(const char *s, int c); 文字列中のトークンへの分解 char *strtok(char *s, const char *delim); その他 int strcoll(const char *s1, const char *s2); size_t strcspn(const char *s, const char *reject); char *strfry(char *string); char *strpbrk(const char *s, const char *accept); char *strsep(char **stringp, const char *delim); size_t strspn(const char *s, const char *accept); size_t strxfrm(char *dest, const char *src, size_t n);
1: /* 2: string-cpycat.c -- 文字列のコピーと結合(悪い例) 3: ~yas/syspro/string/string-cpycat.c 4: Start: 2002/04/21 23:02:39 5: */ 6: 7: #include <stdio.h> /* stderr */ 8: #include <stdlib.h> /* malloc() */ 9: #include <string.h> /* strcpy(), strcat(), strlen() */ 10: 11: main() 12: { 13: char buf[100]; 14: strcpy(buf,"hello"); 15: strcat(buf,",world"); 16: strcat(buf,"\n"); 17: if( strlen(buf) >= 99 ) /* too late */ 18: { 19: fprintf(stderr,"buffer overflow\n"); 20: exit( 1 ); 21: } 22: printf("%s",buf ); 23: free( buf ); 24: }
注意:この例は、悪い例なので真似をしてはいけない。
strcpy() で初期化して、strcat() で後ろに付け加えていく。
100 バイトのバッファでは、buf[0] からbuf[98]まで文字のデータが入れられる。 最後のbuf[99] には、文字ではなく0 を入れる。buf[100] は使ってはいけない。 (次の別のデータが入っている。)
strlen(buf) が 100 を越えた時には、使ってはいけない場所を使ってしまった ことを意味する。これを、「バッファ・オーバーフロー」という。 そもそも事後にチェックするのは、本当は遅い。そもそも、バッファ・オーバー フローが起きないように気を付けながらプログラムを書くべきである。
1: /* 2: string-snprintf.c -- snprintf を使った文字列のコピーと結合 3: ~yas/syspro/string/string-snprintf.c 4: Start: 2002/04/21 23:02:39 5: */ 6: 7: #include <stdio.h> /* stderr, snprintf() */ 8: #include <stdlib.h> /* malloc() */ 9: 10: main() 11: { 12: char buf[100]; 13: if( snprintf(buf,100,"hello%s\n",",world") >=sizeof(buf) ) 14: { 15: fprintf(stderr,"buffer overflow\n"); 16: exit( 1 ); 17: } 18: printf("%s",buf ); 19: } 20:snprintf() は、第1引数に、バッファの番地、第2引数にバッファの長さ(最 後の0を含む)をとる。第3引数以降は、printf() と同じである。snprintf() では、けっしてバッファ・オーバーフローは起きない。起きそうになると、負 の数を返す。(成功すると、バイト数(最後の0は含まない)を返す。)
snprintf() は、%s 以外に %d や %c も使える。
注意:Linux (Glibc を使っている)では、snprintf() の仕様が変更された。 古い仕様(glibc 2.0.6以前)では、snprinf() は、エラーが起きると -1 を返 す。新しい仕様(glibc 2.1以降)では、snprintf() は、必要なバイト数、すな わち、バッファが無限大であるときに書き込まれる文字列の長さ(最後の0を除 く)を返す。これは、strlen() が返すものと同じである。従って、新しい snprintf() では、結果とバッファの大きさを比較して正確に書き込まれたか を検査する。(snprintf() は、決してバッファ・オーバーフローを起こすこと はないが、バッファが足りない時には意図していない結果が保存されているこ とになる。)
1: /* 2: CharValue.java -- 文字の Unicode の値を表示する 3: ~yas/syspro/string/CharValue.java 4: */ 5: 6: class CharValue { 7: static char h1[] = { '0','A','a','漢','字' }; 8: public static void main( String args[] ) 9: { 10: for( int i=0 ; i<05 ; i++ ) 11: { 12: char ch = h1[i] ; 13: int ch_intval = ch ; 14: String dstr = Integer.toString( ch_intval, 10 ); 15: String xstr = Integer.toString( ch_intval, 16 ); 16: stdout.println(ch+" "+dstr+" 0x"+xstr); 17: } 18: } 19: static java.io.BufferedReader stdin = 20: new java.io.BufferedReader( new java.io.InputStreamReader(System.in) ); 21: static java.io.PrintStream stdout = System.out; 22: static java.io.PrintStream stderr = System.err; 23: }実行結果。
% javac CharValue.java% java CharValue
0 48 0x30 A 65 0x41 a 97 0x61 漢 28450 0x6f22 字 23383 0x5b57 %
![]()
~yas/syspro/string/StringCharValue.java
static String s1 = "0Aa漢字"; ... char ch = s1.charAt(i);
1: /* 2: FileCharValue.java -- ファイルの中の文字の Unicode の値を表示する 3: ~yas/syspro/string/FileCharValue.java 4: */ 5: 6: import java.io.*; 7: 8: class FileCharValue { 9: public static void main( String args[] ) throws java.io.IOException 10: { 11: if( args.length != 1 ) 12: { 13: stderr.println("Usage: % java FileCharValue filename"); 14: System.exit( 1 ); 15: } 16: String filename = args[0]; 17: InputStream fis = new FileInputStream( filename ); 18: Reader isr = new InputStreamReader( fis ); 19: BufferedReader br = new BufferedReader( isr ); 20: int ch_intval ; 21: while( (ch_intval=br.read()) >= 0 ) 22: { 23: char ch = (char)ch_intval ; 24: String dstr = Integer.toString( ch_intval, 10 ); 25: String xstr = Integer.toString( ch_intval, 16 ); 26: stdout.println(ch+" "+dstr+" 0x"+xstr); 27: } 28: } 29: static java.io.BufferedReader stdin = 30: new java.io.BufferedReader( new java.io.InputStreamReader(System.in) ); 31: static java.io.PrintStream stdout = System.out; 32: static java.io.PrintStream stderr = System.err; 33: }実行結果。
% javac FileCharValue.java% cat file.text
0Aa漢字 % od -xc file.text
0000000 4130 b461 bbc1 0afa 0 A a 264 301 273 372 \n 0000010 % java FileCharValue file.text
0 48 0x30 A 65 0x41 a 97 0x61 漢 28450 0x6f22 字 23383 0x5b57 10 0xa %
![]()
コンストラクタ String s = ""; // String s = new String(); String(byte[] bytes) String(String original) String(StringBuffer buffer) 文字列の連結、取り出し s1 = s2 + s3; // s1 = s2.concat(s3); String substring(int beginIndex) String substring(int beginIndex, int endIndex) 文字列の長さを調べる int length() 文字列の比較 int compareTo(String anotherString) int compareToIgnoreCase(String str) 文字列中の文字や文字列の検索 char charAt(int index) int indexOf(int ch) int indexOf(int ch, int fromIndex) int indexOf(String str) int indexOf(String str, int fromIndex) int lastIndexOf(int ch) int lastIndexOf(int ch, int fromIndex) int lastIndexOf(String str) int lastIndexOf(String str, int fromIndex) 文字列中のトークンへの分解 String[] split(String regex) その他 char[] toCharArray() static String valueOf(x) // x は boolean, char, char[], double, float, int, long, Object文字列を得るには、「文字列 + x」の形もよく使われる。 toString() メソッドが自動的に呼ばれる。 split() より java.util.StringTokenizer() が使いやすいことも多い。
C言語の sprintf() 相当の書式つきで文字列にするには、Format を使う。 たとえば、整数で sprintf() の %2d ならば、次のようにする。
NumberFormat nf = NumberFormat.getInstance(); nf.setMaximumIntegerDigits(2); int n ; String s = nf.n ;
次のプログラムは、
x = "a" + 4 + "c"次のようにコンパイルされる。
StringBuffer sb = new StringBuffer(); sb.append("a"); sb.append(4); sb.append("c"); x = sb.toString();次のような操作がある。
内部の操作 append() delete() replace() insert() setCharAt() deleteCharAt() アクセス length() toString() charAt() indexOf() lastIndexOf() length() getChars() substring() その他 setLength() capacity() ensureCapacity() reverse()
% echo "hello,world" | tr '[a-z]' '[A-Z]' HELLO,WORLD %この例では、echo のプロセスと、tr のプロセスは、パイプで接続されている。 パイプは、open() したファイルと同じようにread() したり write() したり することがでる。しかし実際には、ファイルではなく、プロセスとプロセスが データを交換する仕組(プロセス間通信の仕組)の1つである。片方のプロセ スが write() したデータを、もう片方のプロセスがread() できる。![]()
パイプにより提供されるプロセス間通信のサービスは、 (単方向の)ストリームとも呼ばれる。FIFO (First In, First Out)や Queue (待ち行列) と呼ばれることもある。
図? バッファによるパイプの実現
パイプを作るには、pipe() システム・コールを使う。これで、パイプ が1本作られ、2つのファイル記述子(読込み用と書込み用)が返される。
1: /* 2: pipe-rw-nodup.c -- pipe() を使ったプログラム(dupなし) 3: ~yas/syspro/proc/pipe-rw-nodup.c 4: Start: 1997/05/26 20:43:29 5: */ 6: 7: #include <stdio.h> 8: #include <unistd.h> /* pipe() */ 9: #include <sys/types.h> /* pid_t */ 10: 11: extern void parent( int fildes[2] ); 12: extern void child( int fildes[2] ); 13: 14: main() 15: { 16: int fildes[2] ; 17: pid_t pid ; 18: 19: if( pipe(fildes) == -1) 20: { 21: perror("pipe"); 22: exit( 1 ); 23: } 24: /* fildes[0] -- 読み込み用 25: * fildes[1] -- 書き込み用 26: */ 27: if( (pid=fork()) == 0 ) 28: { 29: child( fildes ); 30: exit( 0 ); 31: } 32: else if( pid > 0 ) 33: { 34: parent( fildes ); 35: wait( 0 ); 36: } 37: else 38: { 39: perror("fork"); 40: exit( 1 ); 41: } 42: } 43: 44: void parent( int fildes[2] ) 45: { 46: char *p, c ; 47: close( fildes[0] ); 48: p = "hello,world\n" ; 49: while( *p ) 50: { 51: c = *p ++ ; 52: write( fildes[1],&c,1 ); 53: } 54: close( fildes[1] ); 55: } 56: 57: void child( int fildes[2] ) 58: { 59: char c, c2 ; 60: close( fildes[1] ); 61: while( read(fildes[0],&c,1) >0 ) 62: { 63: c2 = toupper(c); 64: write( 1, &c2, 1 ); 65: } 66: close( fildes[0] ); 67: }実行例。
% cp ~yas/syspro/proc/pipe-rw-nodup.c . % make pipe-rw-nodup親プロセス(関数parent()を実行)は、パイプの読込み側(fildes[0])を閉じる。 そして、文字列から1バイトずつ取り出し、0 が現れるまで、パイプの書込み 側(fildes[1])に書込む。cc pipe-rw-nodup.c -o pipe-rw-nodup % ./pipe-rw-nodup
HELLO,WORLD %
![]()
子プロセス(関数child()=を実行)は、パイプの書込み側(fildes[1])を閉じる。 そしてパイプの読込み側(fildes[0])から1バイト取り出す。これをtoupper() で大文字に変換して、標準出力(1)に書き出す。
図? pipe() システムコール実行前 図? pipe() システムコール実行後 図? fork() システムコール実行後 図? 親子で close() システムコール実行後
dup() よりも、dup2() の方が便利である。
1: /* 2: pipe-rw.c -- pipe() と dup() を使ったプログラム 3: ~yas/syspro/proc/pipe-rw-dup.c 4: Start: 1997/05/26 20:43:29 5: */ 6: 7: #include <stdio.h> 8: #include <unistd.h> /* pipe() */ 9: #include <sys/types.h> /* pid_t */ 10: 11: extern void parent( int fildes[2] ); 12: extern void child( int fildes[2] ); 13: 14: main() 15: { 16: int fildes[2] ; 17: pid_t pid ; 18: 19: if( pipe(fildes) == -1) 20: { 21: perror("pipe"); 22: exit( 1 ); 23: } 24: /* fildes[0] -- 読み込み用 25: * fildes[1] -- 書き込み用 26: */ 27: if( (pid=fork()) == 0 ) 28: { 29: child( fildes ); 30: exit( 0 ); 31: } 32: else if( pid > 0 ) 33: { 34: parent( fildes ); 35: wait( 0 ); 36: } 37: else 38: { 39: perror("fork"); 40: exit( 1 ); 41: } 42: } 43: 44: void parent( int fildes[2] ) 45: { 46: char *p, c ; 47: close( fildes[0] ); 48: close( 1 ); /* 標準出力をパイプに切替える */ 49: dup( fildes[1] ); 50: close( fildes[1] ); 51: 52: p = "hello,world\n" ; 53: while( *p ) 54: { 55: c = *p ++ ; 56: write( 1,&c,1 ); 57: } 58: close( 1 ); 59: } 60: 61: void child( int fildes[2] ) 62: { 63: char c, c2 ; 64: close( fildes[1] ); 65: close( 0 ); /* 標準入力をパイプに切替える */ 66: dup( fildes[0] ); 67: close( fildes[0] ); 68: 69: while( read(0,&c,1) >0 ) 70: { 71: c2 = toupper(c); 72: write( 1, &c2, 1 ); 73: } 74: close( 0 ); 75: }実行例。
% cp ~yas/syspro/proc/pipe-rw-dup.c . % make pipe-rw-dupcc pipe-rw-dup.c -o pipe-rw-dup % ./pipe-rw-dup
HELLO,WORLD %
![]()
図? 親子で close();dup();close() 後
親プロセス(関数parent()を実行)は、まず、パイプの読込み側(fildes[0])を 閉じている。標準出力(ファイル記述子 1) を閉じ、dup() で、fildes[1]をコ ピーしている。この時点で、write(fildes[1],...) としてもwrite(1,...,) としても同じ意味になっている。ここで不要な fildes[1] を閉じている。そ して、文字列から1バイトずつ取り出し、0 が現れるまで、標準出力(ファイ ル記述子 1)に書込む。
子プロセス(関数child()=を実行)は、まず、パイプの書込み側(fildes[1])を 閉じている。 標準入力(ファイル記述子 0) を閉じ、dup() で、fildes[0]をコ ピーしている。この時点で、read(fildes[0],...) としてもread(0,...,) としても同じ意味になっている。ここで不要な fildes[0] を閉じている。 そして標準入力(ファイル記述子 0)から1バイト取り出す。これをtoupper() で大文字に変換して、標準出力(ファイル記述子 1)に書き出す。
実験では、次の全ての組み合わせでデータが正確に読み書きできることを確か めなさい。
Linux や *BSD の場合、ライブラリ関数のソース・プログラムは、探せば見つかる。こ の課題では、それを利用してもよい。ただし、それらのプログラムは、他のラ イブラリ関数を呼んでいるものがある。従って、他のライブラリ関数を呼び出 さないように、人手でインライン展開(別の関数の本体を呼び出し側に書く) しなさい。
string ライブラリの中で、strcpy(), strcat(), strlen() 以外の関数を使う プログラムを書きなさい。
main() { char *buf ; read( 0, buf, 1 ); printf("%c\n",*buf ); }この誤りを、次の2つの方法で修正しなさい。
先にパイプを2つ作ってから2回 fork() してもよい。パイプを1つ作って fork() してから もう1つパイプを作って fork() するという方法でもよい。
ヒント: 使わないパイプのファイル記述子は、全部 close() すること。パイプの書き 込み側のファイル記述子が開いている間は、read() しても EOF (End Of File) にならない。自分自身で write() する可能性もあることに注意する。
ヒント:書き手がいないパイプは、全ての書込み側のファイル記述子を closeすると作れる。
ヒント:プロセスを fork() しなくても、パイプに書くことはできる。プロセ スが1個の状態で、バッファの大きさ以上のデータを書き込むと、プロセスが ブロックされる(先に進まなくなる)。引数で与えて与えられたバイト数だけ パイプに書込むプログラムを作り、何バイトからブロックされて止まるかを調べる。 止まったプログラムは、^C で強制終了させる。
1バイトずつ増やしていくのではなくて、2分探索をつかいなさい。大きいバ イト数から始める。止まれば、バイト数を半分にする。動けば、今のバイト数 と前にとまったバイト数の中間のバイト数にする。
FILE *popen(const char *command, const char *type); int pclose (FILE *stream);これを利用して、プロセスを作成し、プロセスにデータを与えたり、あるいは、 プロセスからデータを受け取るプログラムをつくりなさい。
たとえば、expr コマンドを実行して、その結果を受け取るプログラムをつく りなさい。expr は、次のように引数で与えられた式を評価し、結果を標準出 力に返すものである。
% expr 1 + 2 3 %この課題では、expr コマンドに似たようなプログラム作るのではなく、それ をそのまま利用して、結果を受け取るプログラムを作る。実行するプログラム としては、expr 以外に次のようなものが考えられる。![]()