LoginSignup
0
2

More than 1 year has passed since last update.

ふつうのLinuxプログラミングをやってみる その6

Last updated at Posted at 2020-11-05

有名な本らしいので買ってみました
ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道

ふつうのLinuxプログラミングをやってみる その1
https://qiita.com/uturned0/items/b9ae846f2aff5865c074
ふつうのLinuxプログラミングをやってみる その2
https://qiita.com/uturned0/items/56beac990cdd6f1059ed
ふつうのLinuxプログラミングをやってみる その3
https://qiita.com/uturned0/items/675092da8aa89c4b1ff0
その4
https://qiita.com/uturned0/items/8f5765cfc0f0be8a1981
その5
https://qiita.com/uturned0/items/ab97deb489c994a836da
その6
https://qiita.com/uturned0/items/b7df48e87ae9170f3698
その7
https://qiita.com/uturned0/items/263151cf7c83bff7fac1
その7−3
https://qiita.com/uturned0/items/3fbf3ed6bb2a47325f59


chapter 6

system callはめっちゃ遅い。1KB以上の単位で読まないと遅い。それをカバーするのが
stdio = standard i/o library, スタジオじゃなかったんんだ!

byte単位ではなく1行読むとかってことができる

fdからread()でまとめてbytesを取得→stdioがバッファ→programが1 byteくれといったら、そこだけ返す
キャッシュ的な漢字科

書き込みも同じ。ある程度はstdioがバッファリングして、まとまったらwrite()のシステムコールで書き出す

output先が端末なら改行単位で、unbuffer mode man setvbuf ならすぐ出す。
stderrは常にunbuffer mode. だから早い。

typedef FILEがstdioのfd ID的なもの。中にbufferも入ってるが意識しなくていい

fd = 0, STDIN_FILENO, stdin
fd = 1, STDOUT_FILENO, stdout
fd = 2, STDERR_FILENO, stderr

fopen(3)

おおfopenだ。上位言語でもみるよね。


NAME
     fopen, fdopen, freopen, fmemopen -- stream open functions

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <stdio.h>

     FILE *
     fopen(const char * restrict path, const char * restrict mode);

pathを渡すとopen()してそれを管理するFILE型へのポインタを返す。

第2引数はopen()とだいたい同じ

fclose()で閉じる

C言語の英語書籍はbyteのことを文字と呼ぶが、マルチバイト言語のうちらには大きな問題がある。バイトと文字は別物。なるほど。

fgetc(3), fputc(3)

返り値はint, EOFのときは -1 が返る

getc(3), putc(3)

これはfgetcと同じだけど、マクロで書かれてる。昔は早かったが今は違いがない。

マクロって何だっけ? 忘れちゃった。

defineとはプリプロセッサへの指示内容のひとつです。ブリプロセッサとは、コンパイル前に処理を行うプログラムのことです。コンパイル時の前処理として、マクロ名で指定した文字列を定数や式で置き換えます。この置き換えのことをマクロ置換といいます。またこの処理のことをマクロ処理といいます。マクロ処理を定義するためにdefineを使用します。
https://www.sejuku.net/blog/25927

defineのことだった。c-langだとdefineで定数だけじゃなくて関数も変数も宣言できる。コンパイル前に値が確定するので、環境依存の変数とか(64bitとかx86とか)たぶんそういうのをあれするのに便利なんだろう。

ungetc(3)

読み取ったバイトをストリームに戻せる。先読みしてやっぱりいらなかった場合に戻す時に番う。

1234+-5678 の数字を読みたい場合、5まで読んで次の数字が始まったと気付く。5をストリームに戻して、次のループに回す。なるほど

stdioでcatを作る

code

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
    int i;
    for (i=1; i<argc; i++) {
        FILE *f;
        int c;
        f = fopen(argv[i], "r");
        if(!f) {
            perror(argv[i]);
            exit(1);
        }
        while ((c = fgetc(f)) != EOF) {  // EOFに当たるまでfgetcし続ける
            // if (putchar(c) < 0) exit(1);
            putchar(c);
        }
        fclose(f);
    }
    exit(0);
}


実行


$ gcc -Wall -O2 scat.c && ./a.out test
abc
def
xyz
012
あいうえお
かきくけこ

(c = fgetc(f)) は、fgetc(f) の結果を c に入れる。やってみると文字コードが帰ってきていた。
abc\n を入れた時、こうなる

97a98b99c10

97がaで98がbで。その数字を文字にするのが...あれ、これどこでwrite()してるんだ?あれ?
ああ。 putchar(c) がwrite()なのか。条件式の中にいるけど必ず実行されるから。なるほど。
こういうときはmanするって覚えたもんね。


PUTC(3)                  BSD Library Functions Manual                  PUTC(3)

NAME
     fputc, putc, putc_unlocked, putchar, putchar_unlocked, putw -- output a character or word to a stream

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <stdio.h>

     int
     fputc(int c, FILE *stream);

output a character or word to a stream て書いてた。

putcharのエラー処理しなくても動くけど、相手がpipeだとエラーが起こるらしい。やってみたけどエラーは起こせなかった。

fgets(3)

1行ずつ読めるんだけど血管がある。正常に1行読んだのか、バッファいっぱいまで書き込んで止まったか区別できない。対応策はgetc()で自作すること。size-1 byteまでしか読まない、末尾に'\0'を書き込むから、ここらへんよくわからんかった

gets()

絶対使っちゃダメ。バッファ・オーバーフローする。

FGETS(3)                 BSD Library Functions Manual                 FGETS(3)

NAME
     fgets, gets -- get a line from a stream

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <stdio.h>

     char *
     fgets(char * restrict str, int size, FILE * restrict stream);

     char *
     gets(char *str);     <---------

お、タイトルがBSD libraryになった。ほえぇ。 gets(char *str) はサイズの指定がない。つまりポインタできた *str に際限なく書き込む。*strが8bitだったとしてもメモリアドレスとしてしか認識してないので(たぶん)9bit目も書き込んじゃうんだろう。そこに何のデータがあるかはきっと関係ない。超危険。
古い危ない関数は山程ある。マニュアルにあるから使っていいわけじゃない。

fputs(const char *buf, FILE *stream);

行ってわけじゃなくそのままstreamに出す。fgetsと対称のものじゃない。なんだそれ

#include <error.h>
{
    errno = 0;
    if (fputs(buf, f) == EOF) {
        if (errno != 0) {
            // error処理
        }
    }
}

すべてのbyte列を書き終わった時にerrnoが返る?ので、あらかじめ errno=0 にしておいて、 `if (errno != 0) でほんとのエラーか判別する。

errnoってなんなの?

errnoは内でグローバルに定義された整数値です。
errno.h内にはエラーに対応した定数が定義されており、
エラーが起きた場合にerrnoに値を設定するシステムコールや関数があります。
...
このとき、errnoはどのような場合も0に変わることはない為、
処理の前にerrnoに0を代入することで、
その処理のエラーかどうかを判断できます。
...
なお、errnoは一つしか無いため常に書き換えられる可能性があります。
https://qiita.com/ota42y/items/b2f59d7a82c550648677

どうやらc-langの予約語みたいだ(error.h内に定義されてる?)。 でもstdio.hだけのときもperror使えたけどな。

stdio.h内でincludeしてるのかと思ったけどそうでもなかった。

https://code.woboq.org/userspace/glibc/libio/stdio.h.html

perrorの中でimportされてた。

#include <errno.h>
#include <stdio.h>

https://code.woboq.org/userspace/glibc/stdio-common/perror.c.html

でも error.h の中には errno という文字はなくて
https://code.woboq.org/userspace/glibc/misc/error.h.html

誰がどうimportしてるのかはわからなかったけど errno.h というソースは見つけた

# define errno (*__errno_location ())

https://code.woboq.org/gcc/include/errno.h.html

とりあえず、どっかで定義されてるから宣言しないで使える。でも、0で初期化しないとちゃんとエラーと見極められない。

c-langってほんとアレね

int puts(const char +buf);

出力先がstdoutに限定、最後に \n がつく。ただfgets()からつなげるとfgetsも\nつけてくるんで改行がダブる。ほんとあれね。

printf()

%の間に数字入れると幅指定。%0で始めると0パディング。 %- だと左詰めになる。

#include <stdio.h>
#include <stdlib.h>

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

    printf("%010x\n", 77)  ;   // 000000004d
    printf("%10s\n", "abc") ;  //             abc
    printf("%-10s\n", "abc") ;  // abc
    exit(0);
}

result
image.png

printfは便利だけど引数に % が入ってくるとヤバいことが起こるので注意。fgets()のように何が来るかわからないものをそのまま printfするとヤバい。

scanf()もヤバいから使わない

6.5

長い・・・・心折れる。あ、仕事の時間だ。おわり。

酔っ払ってるけど個々に書けば後で思い出せると信じて始める

fread()

size x nmemb 読み込む
number of membersがnmembって。MB的なものかと思った。

read() write() とかシステムコールはあくまでLINUXのシステムコール。UNIX的世界にしかない。fread()ならC-langの世界だから他の世界でも動く。へぇ。

6.6

現在linuxが動くのは32bit machine. おいおいどんだけ古いんだこの本!!!

longて宣言すると32bit, 64bit版が off_t. なんて名前なんだよお前。

define _FILE_OFFSET_BITS 64 入れてビルドするとlong long になるらしい。ならlong longでいいじゃん・・ off_t て・・・。

酔っ払ってるのでバッククオートはめんどくさいです。

6.7

fileno()

     int
     fileno(FILE *stream);

眠いけど全力でmanしてコピペした↑。
streamがラップしてるfdを返す。あれ、streamって引数にするにはどうすればいいんだろう・・・
眠いのでいいや

fdopen()

fdをラップするFILE型の値を新しく作成してポインタを返す。いつなんで使うんだ・・・2つ作ったらcursor別々に動かせるのかな?

file descripterとFILEは違う。あれ違うんだっけ???
fopen()は新しく作るファイルのpermissinoが指定できない。だからopen()してfdopen()でFILEを作る。定石。常考。最近見なくなったねこの言葉

ioctl() fcntl() もstdinにはないから、FILEがほしいから、fdopen()を使う。

もうだめだわからん

fflush()

これ大事そう。streamがバッファしてる内容を即座にwriteさせる。printとかで、改行なしで端末に出力したい時に使う。なるほど出力することをflushと呼ぶのか。書き込みでflush. なんか変だけど。ディスクに書き込めないエラーをstraceしたときとかに目にしそうな気がする。flushすると書き込む。なるほど。

feof()

これよく見る気がする!!と思ったら絶対使うなって書いてる。streamが直前の読み込みでEOFになっていたかどうかを確認するらしい。使うなと言うために書いたらしい。でもよく見る気がするぞ!!

どーゆーことなんと思ったら、EOFの判定はけっこうむずいらしい。まずファイル(FD)がなくてもこいつはfalseするので、while(!feof()) みたいのは無限ループするからその前でケアしてあげなきゃいかん。さらにこの人のコメントすごいくいいんだけど

EOF
Now we get to EOF. EOF is the response you get from an attempted I/O operation. It means that you were trying to read or write something, but when doing so you failed to read or write any data, and instead the end of the input or output was encountered. This is true for essentially all the I/O APIs, whether it be the C standard library, C++ iostreams, or other libraries. As long as the I/O operations succeed, you simply cannot know whether further, future operations will succeed. You must always first try the operation and then respond to success or failure.
https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong?rq=1%EF%BC%89

えーと大事なのは最後の行で簡単に言うと 「書いてみろ読んでみろそしたらわかる」って感じですかねたぶん酔っ払いだけど

初めて仕事で触ったphpで、よくfopen()while(feof) とかコピペしてたけど、今やっと意味がわかった感ある。

clearerr()

tail -f 作る時、stdioはEOF一度拾うともうread()してくれない。もっかいトライさせるためにclearerr()すると、streamのerror flog & EOF flagをクリアしてくれる。なるほど。そうすると、stdioはまたread()してくれる。へぇ。

6.10 strace

straceきたーーーーーーー=!!!!!!!!!!!!!!!!これを待ってました。

hello world をstrace

これを走らせる

$ cat hello.c
#include <stdio.h>

int
main(int argc, char *argv[])
{
  printf("hello");
  return 0;
}

実行

$ gcc heelo.c
$ ./a.out
hello

strace!!!

$ strace ./a.out
execve("./a.out", ["./a.out"], [/* 33 vars */]) = 0
brk(NULL)                               = 0x1a44000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa733d28000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=138375, ...}) = 0
mmap(NULL, 138375, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa733d06000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156160, ...}) = 0
mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa73373a000
mprotect(0x7fa7338fd000, 2097152, PROT_NONE) = 0
mmap(0x7fa733afd000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7fa733afd000
mmap(0x7fa733b03000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa733b03000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa733d05000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa733d03000
arch_prctl(ARCH_SET_FS, 0x7fa733d03740) = 0
mprotect(0x7fa733afd000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7fa733d29000, 4096, PROT_READ) = 0
munmap(0x7fa733d06000, 138375)          = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa733d27000
write(1, "hello", 5hello)                    = 5
exit_group(0)                           = ?
+++ exited with 0 +++

おおお・・なんとなくわかる・・わかるぞ・・・! この本のおかげだぞ・・・!

序盤は libc.so.6とかのライブラリを読んでいる。

mmapはきっとメモリ確保的なアレじゃないだろうか。

write(1, "hello", 5hello)                    = 5

これだ。オレ知ってる。最初の 1 はstdoutだって知ってる。0がstdinで2がstderrやきっと。5が何なのかわからんし5hello意味不明だけどなんとなくわかる。あ、5は5bytes(helloで5文字)かも!!!

自作catをstrace

コード

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static void do_cat(const char *path);
static void die(const char *s);

int
main(int argc, char *arvg[])
{
    int i;
    if (argc < 2) {
        do_cat("/dev/stdin");
    }
    for (i=1; i<argc; i++) {
        do_cat(arvg[i]);
    }
    exit(0);
}

#define BUFFER_SIZE 4

static void
do_cat(const char *path)
{
    int fd;
    unsigned char buf[BUFFER_SIZE];
    int n;

    fd = open(path, O_RDONLY);
    if (fd < 0) die(path);
    for (;;) {
        n = read(fd, buf, sizeof buf);
        if (n < 0) die(path);
        if (n == 0) break;
        if (write(STDOUT_FILENO, buf, n) < 0) die (path);
    }
    if (close(fd) < 0) die(path);
}

static void
die(const char *s)
{
    perror(s);
    exit(1);
}

短いファイルを呼ぶ


$ ./a.out txt
abc
xyz

straceしてみる

$ strace ./a.out txt
execve("./a.out", ["./a.out", "txt"], [/* 33 vars */]) = 0
brk(NULL)                               = 0x1505000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc02f188000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=138375, ...}) = 0
mmap(NULL, 138375, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc02f166000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156160, ...}) = 0
mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc02eb9a000
mprotect(0x7fc02ed5d000, 2097152, PROT_NONE) = 0
mmap(0x7fc02ef5d000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7fc02ef5d000
mmap(0x7fc02ef63000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fc02ef63000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc02f165000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc02f163000
arch_prctl(ARCH_SET_FS, 0x7fc02f163740) = 0
mprotect(0x7fc02ef5d000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7fc02f189000, 4096, PROT_READ) = 0
munmap(0x7fc02f166000, 138375)          = 0
open("txt", O_RDONLY)                   = 3
read(3, "abc\n", 4)                     = 4
write(1, "abc\n", 4abc
)                    = 4
read(3, "xyz\n", 4)                     = 4
write(1, "xyz\n", 4xyz
)                    = 4
read(3, "\n", 4)                        = 1
write(1, "\n", 1
)                       = 1
read(3, "", 4)                          = 0
close(3)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
2020-11-06-00:56:52 irteamsu@kuji-localdev-001-pool-jp2v-dev:/kc/hello$

序盤は同じ。後半でread()が出てくるくらいが違いか。同じやん

straceを見やすくする方法

strace -e trace=open,read,write,close

やってみると確かに見やすい

$ strace -e trace=open,read,write,close  ./a.out txt
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832
close(3)                                = 0
open("txt", O_RDONLY)                   = 3
read(3, "abc\n", 4)                     = 4
write(1, "abc\n", 4abc
)                    = 4
read(3, "xyz\n", 4)                     = 4
write(1, "xyz\n", 4xyz
)                    = 4
read(3, "\n", 4)                        = 1
write(1, "\n", 1
)                       = 1
read(3, "", 4)                          = 0
close(3)                                = 0
+++ exited with 0 +++

自作cat その2, stdio版

code

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
    int i;
    for (i=1; i<argc; i++) {
        FILE *f;
        int c;
        f = fopen(argv[i], "r");
        if(!f) {
            perror(argv[i]);
            exit(1);
        }
        while ((c = fgetc(f)) != EOF) {  // EOFに当たるまでfgetcし続ける
            // if (putchar(c) < 0) exit(1);
            putchar(c);
        }
        fclose(f);
    }
    exit(0);
}

strace

$ strace -e trace=open,read,write,close  ./a.out txt
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832
close(3)                                = 0
open("txt", O_RDONLY)                   = 3
read(3, "abc\nxyz\n\n", 4096)           = 9
write(1, "abc\n", 4abc
)                    = 4
write(1, "xyz\n", 4xyz
)                    = 4
write(1, "\n", 1
)                       = 1
read(3, "", 4096)                       = 0
close(3)                                = 0
+++ exited with 0 +++

同じ?と思ったらここが違う!!

read(3, "abc\nxyz\n\n", 4096)           = 9

このreadで 4096 bytes 読んでて、その後readがない!!

さっきのは 4 bytesにしてたから2回readがあったが、stdinは1回しかない。writeと言われるまで残りを保持してる。なるほど!!!

6.11 練習問題

酔っぱらい撃沈。やすみなさい。

頑張る朝。

問題1 tabが来たら \tを、LFが来たら$LFを出すcatにする。

できた。putcharのエラー処理してないのと、わざわざascii codeで書いてるのがよくなさそう。でもputchar以外わからんもんというお気持ちだった

適当なので参考にしないで欲しいソース↓。答えは https://github.com/aamine/stdlinux2-source/blob/master/cat4.c

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
    int i;
    for (i=1; i<argc; i++) {
        FILE *f;
        int c;
        f = fopen(argv[i], "r");
        if(!f) {
            perror(argv[i]);
            exit(1);
        }
        while ((c = fgetc(f)) != EOF) {  // EOFに当たるまでfgetcし続ける
            if (c == 9) { // tabが来たら
                putchar(92); // \ を出す(ascii表から番号が取れる)
                putchar(116); // t を出す(ascii表から番号が取れる)
                continue;
            }
            if (c == 10) putchar(36); // \n がきたら $ を出す(ascii表から番号が取れる)
            if (putchar(c) < 0) exit(1);
        }
        fclose(f);
    }
    exit(0);
}

答え合わせ。

int c なのに if (c=='\n') できるC言語やばくない。整数ちゃうやん。ずるいよ。
fputs使いたかったんだよ。でもint c入れると怒られるんよ。 '\t'はfputで、intはputcharで入れるのか。なんてめんどくさいんだ。キライ。

問題2 stdinから wc -lができるやーつ

できはしたけどbugがあるぽい。

僕のcode↓。 答えは https://github.com/aamine/stdlinux2-source/blob/master/wc-l-stdio.c


#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
    FILE *f;
    int c;
    int num = 0;
    f = fopen("/dev/stdin", "r");
    if(!f) {
        perror(argv[0]);
        exit(1);
    }
    while ((c = fgetc(f)) != EOF) {  // EOFに当たるまでfgetcし続ける
        if (c == 10)    num++;
    }
    printf("%d\n", num++);
    fclose(f);
    exit(0);
}

結果


正解

$ gcc -Wall -O2 wc.c && cat test | ./a.out txt
6
$ wc -l test
       6 test  

不正解

$ wc -l /var/log/system.log
    6943 /var/log/system.log
$ cat /var/log/system.log | ./a.out
6947

ちょっと違う。改行を多く取ってしまっているのはなぜだろうか。

→勘違いだった。syslogが増えてただけだったw

$ wc -l /var/log/system.log && cat /var/log/system.log | ./a.out
    6984 /var/log/system.log
6984

問題の答えがここにあった!
http://i.loveruby.net/stdlinux2/answers.html

答え合わせ。

int c なのに if (c=='\n') できるC言語やばくない。整数ちゃうやん。ずるいよ。
unsigned long n; にしないと大きいファイル使えないね。
最後 ¥nで終わるファイルがあったら、1つ多くカウントしちゃう。
標準入力をって書いてたけどfileにも対応してる。ずるい。僕の本が初版だから問題違うのかも。

よっしゃああああああああchapter 6終わり!!!!!!!!!

C言語のコードが理解できるようになってきたぞ!!!!!!!

wcコマンドのソース見て、main関数どこにあるかわかるくらいにはなったもんね
https://github.com/coreutils/coreutils/blob/master/src/wc.c

中身はまったくわからなかったけどね


この本を読みながらやってます

ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2