ラベル C の投稿を表示しています。 すべての投稿を表示
ラベル C の投稿を表示しています。 すべての投稿を表示

2009年1月15日木曜日

How to use GETOPT function in C

端末で使ういろいろなコマンドは大抵はオプションを付けていろいろな機能を使うことが出来る。そういったプログラムをCで書くのに必要なgetopt関数の覚書。
unistd.hをインクルードしておくこと。

引数を取るプログラムをCで書くとき、mainの宣言は
 int main(int argc,char *argv[])
こんな感じに書くのが普通。*argv[]が**argvとなってるコードも見たことがある。

で、getoptの使い方。
while ((ch = getopt(argc, argv, "ab:")) != -1){
switch (ch){
case 'a':
-aオプションの処理
break;
case 'b':
-bオプションの処理
この時、-bの次の引数へのポインタがoptargに収まっている
break;
}
}


初めてgetoptを使おうとしたときに見落としていてちょっと躓いたのが、getoptの引数"ab:"の部分。
これはaは引数を取らない、bは引数を取る、という具合になる。使うオプションを""で括ってずらずらっと書いてやり、引数を取るものの後ろにだけ:(コロン)を付けてやればいい。
引数を取るオプションを見つけたときにgetoptはgetargというcharポインタに引数を与えてくれる。例えばコマンドに「-b hogehoge」というオプションが与えられていたならば、getoptがbを見つけたときに、hogehogeという文字列がoptargに収まる。

2008年8月31日日曜日

UbuntuでCをコンパイルできるようにする[build-essential]

素のUbuntuだとC/C++言語のためのヘッダファイルなどが用意されていない。stdio.hすらない。
なので、apt-getまたはSynapticからbuild-essentialをインストールする。

$ sudo apt-get install build-essential


インストール後に/usr/include/ディレクトリを見ると、各種ヘッダファイルが納められていることが分かる。

参考:https://answers.launchpad.net/ubuntu/+faq/61

2008年8月6日水曜日

C言語のNcursesで日本語を扱う

Ncursesというのは端末をいろいろ操作するライブラリ。昔、同様のCursesというのがあって、Ncurses(New Curesesの意味)はその後継。
端末上でグリグリ操作するようなアプリケーションは、これを使わなければ実現できない。端末上で動作するviやEmacs、nanoなんかのコマンドラインエディタ(テキストエディタ)もこれを使って実装してあるんだろう。

RubyやPythonなどのスクリプト言語などでもNcurses(またはCurses)が使えるようになってたりする。

C言語でNcursesを使う場合、まずinitscr()で初期化し、最後にendwin()で後処理をする。
これで、addstr()などで端末上の好きな場所に文字列を表示したりできる。
Ncursesは標準のライブラリでないので、コンパイル時にgccには-lncursesオプションを付けなければならないので注意。

そして、日本語(2バイト文字)を扱う場合はちょっと1手間必要なので、ここに書いておく。

まず、ダメな例。1秒間「ほげほげ」とだけ表示して終了するプログラム。
#include <ncurses.h>
main(){
initscr();
addstr("ほげほげ");
refresh();
usleep(1000000);
endwin();
}

これだと、「ほげほげ」が文字化けしてしまう。

日本語(2バイト文字)に対応させるには次のようにコードを書き、なおかつgccには-lncursesオプションではなく-lncurseswオプションを付ける。(末尾にwを付加)
#include <ncurses.h>
#include <locale.h>
main(){
setlocale(LC_ALL,"");
initscr();
addstr("ほげほげ");
refresh();
usleep(1000000);
endwin();
}

これだと、日本語(2バイト文字)が扱える。

wはwide charactorの意味。Ncursesを2バイト文字に対応させたもので、Ncurseswという。

NcursesとNcurseswは別のパッケージとして配布されているので、自分の環境を要確認。
UbuntuのSynapticだとNcursesはlibncurses5,libncurses5-devというパッケージ名。Ncurseswはlibncursesw5,libncursesw5-devだった。

ちなみに、RubyではまだNcurseswには対応していないらしい。そのせいでRubyで書こうと思っていたプログラムを、今Cで書いているのです。。。


<追記>
コードの < と > をエスケープし忘れてたのを直しました。

2008年7月10日木曜日

世界一汚いCコードを読み解く

昨日のエントリ「世界一汚いCコード」を読み解いてみる。

まずは原型。
printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);
これの結果がunixになるのは何故なのか、少しずつ文を読み解いていく。

実はCでは#define unix 1 となっているので
printf(&1["\021%six\012\0"],1["have"]+"fun"-0x60);
配列を扱う書き方においては、x[1] = *(x+1) = *(1+x) = 1[x] なので
printf(&*("\021%six\012\0"+1),*("have"+1)+"fun"-0x60);
&* は意味が無いので
printf(("\021%six\012\0"+1),*("have"+1)+"fun"-0x60);
"\021%six\012\0"の1つ目の\0はNULL文字かと思ってしまうが、Cでは数値の先頭に0を付けると8進数を表すので、021の部分は8進数21という数値であり\021で1文字と見なされる。
ダブルクウォートで括った文字列"string”はstringという文字列の先頭ポインタとなる。
("\021%six\012\0"+1)ならば+1で先頭の位置が一文字ずれて、"%six\012\0"という文字列の先頭ポインタとなる。
ただし、*("have"+1)は*が付いているので、文字列の2文字目の値自体を指す。
printf(("%six\012\0"),'a'+"fun"-0x60);
'a'のASCIIコードは0x61であり、0x60を引くと1となるので、"fun"+'a'-0x60は"fun"+1となる。
さらに先の"\021%six\012\0"+1→"21%six\012\0"と同様に、"un"になる。
printf(("%six\012\0"),"un");
NULL文字(\0)は文字列の末尾を示すので、"%six\012\0"は実質"%six"
printf(("%six"),"un");
%sは第二引数の値と入れ替わるので、
printf("unix");


汚いっていうか、如何に読みづらくしているか、っていうコンテストじゃねコレ?

2008年7月9日水曜日

世界一汚いCコード

大学で、たまたま「汚いCコードコンテスト」の話になった。

例えばこれ。
main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
1987年の入賞作品だそう。
http://www0.us.ioccc.org/1987/korn.c
実行結果は「unix」と表示される。

何故そうなるのか初見ではまったく分からない orz

このコードを読み解くためのヒントなんていうものが一緒にあった。
http://www0.us.ioccc.org/1987/korn.hint

ヒント1:unix
unixというシンボルはコード中で宣言も定義もされていない。unixは何を意味するのか。

ヒント2:"have"
"have"というのが出てくるが、""で括ってあるので文字列かと思いきや、さらに[]で括ってある。

ヒント3:[]
Cではx[1]と書くのは、*(x+1)の省略形になっている。数学的に考えれば、*(1+x)でも意味は同じだ。じゃあ、1[x]とx[1]は同じじゃないか。

本当はコードを読み解いていこうと思ったけど、今日は力尽きてしまったので終了 orz
また後で追記するかも。

---------------------------------
7月10日、追記しました。
世界一汚いCコードを読み解く:
http://mstssk.blogspot.com/2008/07/c_10.html