quaternionの日記: C言語のポインタの威力と美しさを示せる最も短く理解しやすいコード 32
日記 by
quaternion
Quora日本語版に「C言語のポインタの威力と美しさを示せる最も短く理解しやすいコードは何ですか?」という質問があった.ヌル終端文字列のコピー(whileの条件式でコピーするやつ)は自明すぎて面白くないし推奨されるものでもないので,main関数の引数処理を書いてみた.意図したのはargcとargvの更新を一箇所にまとめることだ.
普通はライブラリを使うよねという話もあると思うが,まずい書き方だったたらご批判をこいたい.
argcは要らない (スコア:1)
argv[argc]がNULLであることが保証されているので、
ポインタアクセスするならargcは不要で、
でいいかと。
あとは、少し冗長ですが
と、「argv[0]は読み飛ばしている」ことを明示する方が自分の好みですが、* と ++ の優先順位で悩む人向けには*++argvの方が親切かも。
Re:argcは要らない (スコア:2)
最近の人はコマンドラインプログラム描かないから
while((c = getopt(argc,argv,OPTION)) != EOF) {
switch(c) {
.
.
default:
usage(argv[0]);
break;
}
}
if (argc >= optind) {
.
.
.
} else {
for (i = optind ; i
の世界はないか…
ポインタ関係ねぇ
Re:argcは要らない (スコア:2)
argcが0の場合を考えてしまう。
Re: (スコア:0)
考える意味あるの?
Re:argcは要らない (スコア:2)
ある。
/*
argv[1]がNULLである保証は無いし、その場合にputs(argv[1])するのは、すごく嫌だ。
*/
Re: (スコア:0)
いや、それは分かるけど、argcが0である例が具体的にあるのかと。
Re:argcは要らない (スコア:1)
今ちょっとググったら、ソースは知恵袋ですが昔のMacで Code Warrior のスタートアップはargc==0だったらしい [yahoo.co.jp]です。
「コマンドライン引数を渡すことができてprintfが使えるけど、argc==0になりえる」ようなホスト環境なんて実在するとは思えませんが、「規格上はargc==0が許される」のは確かですし、どうしても気になるなら「assert(argc>0);」するぐらいでいいんじゃないですかね。
とりあえず (スコア:0)
mainの引数にconst修飾できるのは処理系定義ではありませんでしたっけ(独自拡張としては許されるが処理系のマニュアルで文書化されていなければならない)。たいていの現実の処理系はリンク時に関数名しか見ないと思いますが、移植性が保証されているわけではないような。
Re:とりあえず (スコア:2)
少なくともlinux環境は
int main(int ,char **);
ですね。
argvの中を書き換えてもSEGVは起こりません。
Re:とりあえず (スコア:1)
Re:とりあえず (スコア:2)
たしかそうですね。
どういう風に特殊だったか、今ひとつ思い出せない。
#なんかでwarningだしてた。
Re:とりあえず (スコア:1)
Re:とりあえず (スコア:2)
あ、environmentがつけられるんだった。
最近getenvしか使わないんで。
Re: (スコア:0)
int hoo(int x); /*プロトタイプ宣言*/
int hoo(const int x) { /*実装ではconstをつける*/
/* 省略 */
}
と言うコードが書けるぐらいでconst はコンパイル時の静的チェックを行うだけです
そのメモリがROMのようなread onlyな領域かそうでないかと言う話はOSとかハードウェアの仕事であって
constにはそれ以上の意味はありません
Re: (スコア:0)
int hoo(char** x); /*プロトタイプ宣言*/
int hoo(const char* const* x) { /*実装ではconstをつける*/
return x;
}
でやってみそ。エラーになるから(少なくともgccとclangではエラーになったし規格上もエラーになるのが正しいはず)。だから
int main(int argc, const char *const *argv) {
が通るのは独自拡張と考えるしかない。
何の関係もない例を持ち出して「意味はありません」とか言われても。
Re:とりあえず (スコア:1)
Re: (スコア:0)
その代わり、規格に
int main(void) { /* ... */ }
または
int main(int argc, char *argv[]) { /* ... */ }
またはこれらと同等(仮引数名が違うだけとかtypedefを使うとか)の宣言か、
上記以外の処理系定義の何かでなければならないと書かれている。
const char* const*はchar**と同等とは言えないので、最後の処理系定義の宣言としか考えられない(ただしそれなら文書化されている必要がある)。
Re: (スコア:0)
あ、return x;は間違いだけど、これを削除しても結果は変わらない。
冗長 (スコア:0)
printf("%s\n", *(++argv));
ってする。
凄いかなあ? (スコア:0)
argvに展開するところやargcを計算するところが凄いんであって、
それを利用するところは短くて美しいかもしれないけれど凄くはないような。
感心した例 (スコア:0)
数値を16進数の文字に変換するやつ。
"0123456789ABCDEF"[data & 0x0f]
Re: (スコア:0)
ポインタ……?
# こういう勘違いを生むあたり、「C言語のポインタ」はそもそも美しくないと思う
Re:感心した例 (スコア:1)
Cの配列はポインタ演算の糖衣構文だとか何とか
Re: (スコア:0)
*("0123456789ABCDEF" + (data & 0x0f)) と完全に等価
Re: (スコア:0)
完全に?
その美的感覚にはついていけないな。
const は蛇足 (スコア:0)
const char *const *argv
を本当に美しいと思う人は、C言語に毒されているだけです
普通に考えたらこんなコード読みにくくてしょうがないですよ
そもそもポインタの美しさを示すために
わざわざ const を付けるのは蛇足でしかありません
void *p = 0;
ぐらいで十分です
美しくはない (スコア:0)
よく混乱を招く
const char * const p
のpを変更しちゃいかん
威力はある
pとqが同じ配列のどれかの要素を指す場合(mallocされたものでもいい)
pとqの大小比較ができる
p-qが意味を持つ
これらはC言語特有の性質
Re: (スコア:0)
ポインタとintの変換ができる
というのもあるな
ある種のデータ構造を作るのに便利
ポインタの検索や、二重リンクリストで差分だけを持つやつとか
Re: (スコア:0)
ああ、最後は*pね
失礼しました
Re: (スコア:0)
> よく混乱を招く
自己紹介かな?
> const char * const p
> のpを変更しちゃいかん
このpを変更するのは問題ない。この例で変更できないのは*pと**p
Re: (スコア:0)
間違えた
const char * const* p
のpは変更しても問題ない。
Re: (スコア:0)
お前らコントやってんじゃねえよ(笑)