Skip to main content

ログ取得ツール

Category: プログラミング

を渡しますにより、printf

gccの日本語メッセージ。

hogehoge.c: 関数 `func' 内:
hogehoge.c:15: 警告: 引数 1 個の `arg' を渡しますにより、キャストなしでポインタから整数を作りました

funcという関数の呼び出しを書くときに1個目の引数の型が違ったので明示的にキャストされていないけど勝手にキャストしてポインタを整数にして渡しちゃったけど、渡された先で何が起ころうと知ったことじゃありません、という警告だ。 不自然とかそういう問題じゃないように思う。ずいぶん前からこうなってるんだけど、指摘しなきゃ誰も気づかないのかな。もしかしたら、おもしろいからそのままにしているだけかもしれないね。あるいは、makeの「入りますディレクトリ」が見えてるから、gcc側がおかしくても気にならないのかもしれない。 で、やはり疑問なのでgccのソースの中にあるja.poを見たら、確かにそうなるのもうなずけるなーと思った。というのは、passing arg %d of %s'を引数%d個の%s’を渡しますと訳すのは間違いじゃない(実際の意味は個数じゃないけど)。そして%s makes integer from pointer without a castを%sにより、キャストなしでポインタから整数を作りましたと訳すのも間違いじゃない。でもつながらない。 別に「〜により」にこだわることはなくて、passing arg %d of %s'は「%2$s’への%1$d番目の引数が原因です。」にして%s makes integer from pointer without a castは順序を変えて「キャストなしでポインタから整数を作りました。%s」とかにするのが自然かな。つなげると「キャストなしでポインタから整数を作りました。func'への1番目の引数が原因です。」になる。でも順序を変えたりすると文句を言われそうだし、printfの「%n$」なんて知らない人も多いからメンテナンス性が悪くなってしまう。今はソース上は文字列はすぐにprintf()に渡るからいいけど、そうならなくなったら「%n$」は使えなくなる。それに別の文脈で同じ文字列が使われて、そっちではpassing arg %d of %s’は「原因」じゃなかったりするかもしれない。 あとprintfを提供するライブラリ側で「%n$」に対応してなかったらどうするんだって話もある。簡易のlibcを使っている場合や、滅多に使わない機能なのでテストされてなかったりする環境も充分にありうる。 ちなみに、引数を1個スキップするだけなら「%*s」みたいに書ける。つまり、printf("%*s\n", 1, "test");だと「test\n」が出力される。 printfは奥深い。printf(3)のman pageだけ読んで使い方を理解できる人は少数派だろうな。

もいっちょ、別のアプローチ

ひとつ前の話の続き。むかし、checkitというスクリプトを書いたことがあるのを思い出した。これなら、こうなる。

 # checkit 'AC_CHECK_SIZEOF(long double)'
  :
checking for long double... yes
checking size of long double... 12
 # checkit 'AC_CHECK_SIZEOF(FILE)'
  :
checking for FILE... yes
checking size of FILE... 148

もしかして、autoconfがわかる人にとってはこれが無敵なのかな?

ああめんどくさい

long doubleって何バイトなの? sizeof(FILE)っていくつ? 調べようと思えばなんてことはないことを調べるのが面倒。どうにかしてくれ、おまえらコンピュータだろう? そんなあなたの素朴かつ誠実な疑問に応えるためのスクリプトがcprintf.shだ。

 # cprintf.sh 'sizeof(FILE)'
sizeof(FILE)=148(0x94)
 # cprintf.sh -i stdlib.h -i sys/time.h -p 'struct timeval tv;gettimeofday(&tv, NULL);' \
  tv.tv_sec tv.tv_usec
tv.tv_sec=1080281038(0x4063c7ce)
tv.tv_usec=855943(0xd0f87)
 # cprintf.sh -p '#define rdtsc(a) asm("rdtsc":"=A"(a))' -p 'long long a=0;' -p 'rdtsc(a);' \
  '(unsigned int)(a>>32)' '(unsigned int)a&0xffffffff'
(unsigned int)(a>>32)=2433(0x981)
(unsigned int)a&0xffffffff=-161508906(0xf65f91d6)

printfのフォーマット文字列が%d(%#x)に固定されているのがちょいと問題であることがうかがえる。オプション増やそうかな… (追記) 2004-03-26 15:32 別のやり方を書きました。人それぞれ。

戒め

また失敗をやらかしたので、記録しておく。 トップページのRSSの読み込みやらをいろいろ改造して、If-Modified-Sinceを正しく出して処理するようにした。今までもコード自体は書いてあったが、今までうまくいってなかった。というのも、curlのライブラリの問題だと思うが、httpsでのHTTPのリザルトコードが見えなかったのだ。この「httpならよくてhttpsはダメ」という条件が今回の変更以前にはわからず、成功する場合と失敗する場合がどうもよくわからなくて、If-Modified-Sinceを使っていなかった。思うに、ちょっと凶悪だったかもしれない。 でも、今回書く失敗というのはこの話ではない。304(Not Modified)を受けた後の処理である。 キャッシュファイルの更新時刻で取りにいくかいかないか判別しているのだが、304(Not Modified)を返してくれた場合はキャッシュファイルを読みにいく、という処理しか書いていなかった。本来はtouch()でキャッシュファイルの更新時刻を更新しなければならない。つまり何が起きていたかというと、304を返されたRSSやページには毎回アクセスしに行っていたのである。なんだよ、キャッシュになってないじゃん。 キャッシュファイルを処理しているだけにしては遅いことと、アクセスLEDの挙動を見ていて、今日になってはじめておかしいことに気づいた。けっこう迷惑だったろう。

PHPメモ:fwrite()について

PHPでfwirte($f, $str, strlen($str));しても$strが全部書き込まれず、fwrite($f, $str);にすると全部書き込まれる、という現象に会ったことがある。strlen($str)が実際の文字列の(バイト列としての)サイズとは異なる値を返しているからではないかと思っていた。 fwrite()はもともとバイト列(バイナリ可能)で、strlen()はもともと文字列(何らかの符号で書かれたテキスト)なので意味は違うけど、なんか釈然としない。PHPごときのstrlen()なんて文字コードをちゃんと見てるわけでもないし、バグるほどの処理があるとは思えない。 もしかしたら、fwrite()の戻り値をちゃんとチェックしなきゃ最後まで書かれたかどうかは判断できないから、その処理を書いてないせいかもしれないな。長さのパラメータがない場合は途中でやめずに勝手に全部書いてくれるのかも。

Pythonメモ:trは必須だろう

Pythonのstring.translatestring.maketransが情けないので、自分で少し書いてみた。stringmisc.py。デフォルトのstring.translateより少しはマシだと思う。

import stringmisc
print stringmisc.tr("a-z0-9", "A-Z2-90-1", "test0123")

結果は以下のようになる。

TEST2345

ドキュメントのかけらも書いてないのは申し訳ない。 Unicodeにも対応しているので、ひらがな←→カタカナ変換とか全角←→半角変換にも使える。たぶん、ROT13変換とかにも使えるのだろう。そういう、デフォルトの変換セットとか作っておこうかなぁ。 あと、PHPのtrは連想配列(Python用語だと「辞書」?)を引数に取ることができるが、これはstring.tr_array()という別関数にしてみた。連想配列のキーに正規表現を使うかそうでないかのフラグをつけて。ただ、これでもあんまり厳密じゃないんだよなぁ。Pythonの正規表現は意外にも最長マッチではないみたいで、re.split("(a|abc)", "abcde")['', 'abc', 'de']ではなくて['', 'a', 'bcde']になる。 trはre.subの繰り返しでは実現できないわけだから、あらかじめ作っておいてほしいよ。 PHPのtrを知ったときは目から鱗だった。そうだよ、そうなんだよ、と。 (追記) 2004-03-22 16:53 今さら気づいたんだけど、ちょっとバグっていた。range(ord(str[i-1])+1,ord(str[i+1])-1):じゃなくて、range(ord(str[i-1])+1,ord(str[i+1])):だった。-1はいらない。

Pythonメモ

Pythonで文字列から文字コードを求めるのはord()という関数である。逆に文字コードから文字(列)を求めるのがchr()だとされている。…であるからして、

chr(ord(str[0])) == str[0]

は常に真であるように思える。しかし、違う。ord()がUnicodeに対応しているのに対し、chr()は対応しておらず、chr()の引数は0〜256の範囲しけ受けつけず、範囲外の値が渡されるとValueError例外を投げてしまうのだ。 これを回避するには`u"%c" %(val)を使う方法しか私は知らない。

u"%c" %(ord(str[0])) == str[0]

はだいたい常に真のようである。 chr()の引数に文字コード情報がないというのが間違いではないかと思う。`

cpplib

cpplibという気になる単語を目にすることがよくある。gccを-vつきで実行していると、cpp(プリプロセッサ)が呼び出されずにcc1(コンパイラ)が直接実行されている。バージョン情報にはCPPが(cpplib)という注釈つきで出てくるのである。 なにぃー! cppの機能をライブラリで使えるというのか!! しかしgccパッケージを見てもlibcpp.aやcpplib.hは入っていない。くそう、おいしいところを独占しやがって!! ということで、gccのソースを展開してcpplibだけを抜き出そうとしたが、これがなかなかどうして、けっこう大変なようだ。まだ、道半ば。 やはり素人はpopen()を使えということなのかなぁ。 現状、↓のようなプログラムのコンパイルまではどうにか通るようにしたものの、リンクでいろいろunresolved symbolが出まくる。どの.oとどの.oをライブラリにぶち込んだら許してくれるのだろうか…面倒だがいろんな作業だけでどうにかなるかもしれない。 #include <stdio.h> #include “cpplib2.h” int main(int argc, char *argv) { cpp_reader *rd; rd=cpp_create_reader(CLK_GNUC89); cpp_preprocess_file(rd, “cpplib.h”, stdout); return 0; } cppをmain()抜きに拡張するか、gccのcpplibを抜粋するか、果たしてどちらを選ぶのが賢かったのだろうか。 (追記) 2004-03-08 14:58 よく考えたら、m4とかmakeやbashやzshやsedやawkもライブラリにしてほしいかも。ついでに言えば、Pythonもか。PerlやRubyはlibperl.soやlibruby.soがある。使ったことはないがさぞかし便利に違いない。libphp.soはApache HTTPD用のモジュールなんだろうな、たぶん。 そうか、もしlibphp.so(Apache HTTPDのモジュール)が普通にプログラムから使えるんだったらmod_python.soを使えばいいのか。

regparm

gccにregparmというのがあるらしい。普通にやるとIA32での関数呼び出しは全部の引数がスタック渡しになる。その昔x86アセンブラでプログラムを書いた人なら、AXとかDXに引数を置いて戻り値をAXにして…なんてことをやっていたことを思い出すだろうが、それをgccがやってくれるのだ。オプション-mregparm=nで、nがレジスタ渡しをする引数の数ということになる。 無論、*.o毎に-mregparm=nの指定が異なる場合は引数が正しく渡ることはない。 まあでも小さな関数を呼び出すだけの場合は-mregparm=nなんていう、ある種の危険なことをしなくても、単にinline指定するか、そうでなくても-O9などをつけておけば勝手にインライン展開されてスタックどころかcallすら呼ばなかったりする。あと、Alphaとか、レジスタの本数が多いアーキテクチャの場合は最初からレジスタ渡しになってたはずだ(よく覚えていないけどそうに違いない)。 Linux Kernel 2.6.4-rc2-bk1をコンパイルしようとして、オプションにあったので気づいた。もしかしたら、ポピュラーになるのかもしれない。ヘッダのプロトタイプ宣言でregparmの指定を書ければ、ライブラリで使っても互換性をとれるよなぁ。