C言語相談室(上級者専用)
■ このスレッドは過去ログ倉庫に格納されています
>>24 見た。 すげー香ばしい…。 真正の「自称」研究家だわ。 >>27 最近のWin10のは、コマンドプロンプトじゃなくてPowerShellになるらしい。 >>28 知らん。 というかお前らも無意味だと思いつつ続けてるんだろ? 元気なのはいいことだとは思うが。 上級者が初心者/中級者と比べて圧倒的に少ないのは自明だし、過疎るのは仕様。 てか、どの程度からを上級者と言っていいのか決まっているわけでもないし、 そもそもそういう人がこのスレを見に来るとは限らず、また見ても何かを書きたいと 思うとは限らない。 Cのnull安全がRustだと聞いたがRustはそもそもC++の後継であってCは念頭に置いてないと思うんだが 詭弁かな。 そりゃ認識を間違ってるだろ。 https://techacademy.jp/magazine/8735 https://www.tiobe.com/tiobe-index/ C++erはCの後継はC++だと思っているし、俺もそうだと勘違いしていたが、 実際はCとC++は別枠で扱われていることが多いし、その方が妥当だ。 今のスマポ(キリッなC++とCは別物だ。 文法的な点については、全言語の半分くらいはC後継だし。 C->ObjectiveC->swiftこそが正統Cの系統だ!と彼らが考えていても不思議ではないし。 Rustを触ったことはないが、通常議論されているのは、 ・RustはCを代替できるか? であり、「C++を代替できるか?」なんて言っている奴はいない。理由は、 ・C++は現在も旺盛に進化中であり、後継を必要としてない(C++XXの後継はC++YY) ・C++ではCを代替できないのは確定済み(Linux等) で、どうにかしたいのはあくまでCであり、C++ではないからだろ。 実装や機能を見る限りについてはRustはCよりC++に寄ってると思うよ Cにこの抽象度の機能をもたせると必然的にそうなるという説はあるけども 何が言いたいかと言うと後継という立場について、そうであったC++は少なくともCの領域を完全にはカバーしなかった訳で、そのカバーしなかった領域を(C++に寄っている)Rustがカバーしたかと言われるとNoだと思っているからRustはCよりC++の後継という方が妥当に思えるという話 申し訳ないが俺自身がRustを知らないのでつっこんだ議論は空回りしてしまうが。 > そうであったC++は少なくともCの領域を完全にはカバーしなかった訳で、 > Cの領域 これをどう捉えるからだよ。 少なくともC++の連中は「カバーしている」と思っている。 そしてLinusは「C++は全くのゴミだ」と思っている。 良くも悪しくも、Cはミニマムで美しく完成している。全く無駄がない。 そして各後継言語はこれに対して、「便利機能」を追加しようとしてきた。 C++は「クラス」「型検査」「例外」を導入した。 結果、CならCキャストやマクロですぱっと書けるところをグダグダとテンプレートを引き回したり、 或いは間接呼び出しでいちいち実行速度が遅くなったり、 はてまたスマポ(キリッでGCの再実装を盛大にやらかしている有様だ。 Linusの意見はごもっともだ。 Rustの謳い文句は、「実行速度」「ゼロコスト抽象化」等であり、 C++の失敗を見て学んでいることは明白だ。 だから、後継になろうとしている相手はCなんだよ。C++ではなくてね。 とはいえ、そちらの指摘の通り、実際の仕様がCよりもC++に近くなるのは必然だ。 普通にやれば、C++で成功したところはそれをもらって、 C++で失敗したところは新たな方法を提案して、となるだろうからね。 Rustはぱっと見、スマポがデフォで生ポは例外的使用か? C++は歴史的経緯から「言語的には」生ポがデフォでスマポが追加されている。 これは、「『アプリケーション用の』プログラミング言語」としては間違いだ。(Linusもそう言っている) 結果、C++erは「デストラクタ」「スマポ」「=のオーバーロード]をこねくり回して苦労しているが、 本来はこれらは言語側で完全に隠蔽すべき物だ。(ユーザが書く必要なんてない) そしてRustはそれをやり、結果的にnull安全なんだろ。 正しい方向への進化だよ。(分岐元がCかC++かは何とも言い難いが) C++はアプリケーションの動作と関係ないところで無駄に苦労するでしょ。俺はあれが嫌い。 >>44 その言い方、Rustを結構使っているように見えますが、違いますか? Cが「ミニマルだ」ということには——少なくともISO C99規格を見るに——あまり賛同できないんですが、 それはまぁ、置いておいて、私がRustを触った限り、あれはC++の代替に見えます。 C++が現在も(規格を拡張する方向に)積極的に開発されているのは分かっていますし、 「だからRustが取って代わる必要はないのだ」という理論的帰結も理解できますが、 ポインタの安全性ややはり名前空間の宣言あたりを見ると(あくまで触りですが)どうしてもC++の文法を彷彿とさせるの 構造が多いように思いますね。 あ、すいません。一行目で使ったことがないと断られていました。 いや、なぜ確認しなかったんだろう、失礼しました。 >>45 > あれはC++の代替に見えます。 それは正しい。 というか、「後継」と「代替」はこの場合明確に区別して使われるべきだ。 ・「○○の後継」---○○言語を発展させたもの。 上位互換であり、○○言語と共に使うことを考慮されている。 ・「○○の代替」---○○言語の代わりに使うもの。 基本的に○○言語と一緒に使うことはない。排他的使用となる。 Rustの謳い文句は、「効率的なCバインディング」であって、 「効率的な『C++』バインディング」ではないんだよ。 CのDLLは当然呼べるが、C++のDLLを呼べるように出来ているか? C++の「後継」と言うのなら、基本的にはC++の資産を全活用できる方向になってないといけない。 ただしそもそもC++は「後継」用のAPIを整備して無いと思うんだが。 C++の例外「実装」についてググッても、まともな文献が出てこないだろ。 おそらくあれは「実装」まで言及した仕様ではなく、「言語としての動作規定」しかされてないんだ。 (元々C++はそういうノリだし) だからtry-catchを含んだ関数をDLL化できて他言語から呼べるかはかなり疑問だ。 勿論、昔からの課題だから今は解決されているかもしれんが。 > Throwing C++ exceptions across DLL boundaries is only possible when all modules use the same C++ runtime, > in which case they share a heap as well. But this can be a maintenance burden, > especially when libraries from multiple vendors are involved, so it is discouraged. > https://stackoverflow.com/questions/5107948/throwing-c-exceptions-across-dll-boundaries だからCのDLLは他言語(Rust/Ruby/C++/C#)等から直接呼べるが、 C++のDLLを呼べます、と謳っている奴はいないだろ。 この点において、C++は後継言語の存在を許していない。 だからRustはCの「後継」であり、C++の「代替」というのが妥当な見方なんだよ。 実際、RustがあればC++を使う意味がないだろ。 Cは大規模コードに対するサポートが全くない。 とはいえ、「やれば出来るだろ by Linus」なのは事実だが、実際それでは辛いわけで、 コンパイラに任せられるところは任すという方向で上手く手抜きできるように進化させたのがC++だ。 Rustも同じ方向なのだから同様の物になるのは当たり前。 当然、どちらかを使用すれば事足り、RustとC++は排他関係になる。 つまり、RustはCの「後継」であり、C++の「代替」だ。おそらくここまでは大体の人が納得するはず。 問題はCの「代替」にもなり得るか?という点であり、だからそこが議論されているわけだ。 今現在もCを使うメリットは速度面しかない。したがって速度面のデメリットがなければ良く、 「ゼロコスト抽象化」等、速さにこだわっているのはそこなんだよ。 ただ俺個人としては、全体を一つの言語で書く必要なんて全くなくて、 NumPyのアプローチ、つまりどうでもいいところ(9割以上)はスクリプト言語で書き、 必要なところだけCのDLLを呼ぶ、というのが正しい気がするが。C#もこの方向だね。 この場合、リソース管理をGC言語側に任せられ、 また個別関数単位での切り出しになり、 DLL内関数はお互い独立(非依存)で行っても1,000行とかでしかなく、 Cでも全く問題にならないんだよ。 だからRustも微妙に中途半端な方向だとは思うが、 もし仮にCを代替できるのなら、それは素晴らしいと思うよ。 僕の知り合いの知り合いができたパソコン一台でお金持ちになれるやり方 役に立つかもしれません グーグルで検索するといいかも『ネットで稼ぐ方法 モニアレフヌノ』 8UTJJ RustはCの「後継」であり、C++の「代替」 これでRust周りの(宗教戦争じみた)論争の理由が理解できた。 ポインタってさ、「指示子」と和訳したら分かりやすいと思うんだが そうしなかった理由ってなんだろ それとも俺の感性がおかしいだけか え? なに? もしかして昔なにか荒れる原因になった ANDOR 問題のある人が使ってた用語なのか。 若いもんで知らなかったわ すいません。 「指示子のgcc拡張」みたいな早口言葉モドキができて面白いかも。 指示子の指示子 指示子の配列 配列の指示子 配列の指示子の指示子 指示子の配列の指示子 ダブル指示子 配列の指示子の配列 👀 Rock54: Caution(BBR-MD5:1341adc37120578f18dba9451e6c8c3b) C99(ISO/IEC 9899:1999)で,main()関数の型がintな理由ってどこかに書いてあるっけ。 今ふと,リターンコードって0から255の整数なんだから uint8_t main(... としても問題ないよなと思ってさ。 すまん。途中で送信してしまった。 そうするとコンパイラにmain()の型はintだと怒られた。 型がintというのは決まってる。 実際の範囲は実装依存。windowsだと違ってなかったか? main()が戻す値は呼び出す側の問題だとは思うが、その部分(crt0とか)は普通はC言語に合わせてintを 返してくると想定して作られていると思う。しかしその部分まで自作するというのであればなんでもアリではある。 https://en.wikipedia.org/wiki/Crt0 シェルが受け入れるコマンドの終了ステータスの値が0-255の範囲、 てのはUNIX系もDOS系も(珍しく)一致してるのね。 unsigned char の外に出ないことは間違いないわね。 ANSI以前の古いCでの「宣言なしに使われた外部関数はintの値を返す」 という仕様が、規格化したときに互換性の問題を生まないように main()の返り値はintと決めたんじゃないかしら。 なるほど。まあ過去互換性は重要だしね。 ただ,CはPOSIXというOSの標準規格を定めてる団体が関与して設計されてるから「リターンコードは0--255。よってmain関数の型はuint8_t」と割り切ってくれてもいいのになぁ。 とか勝手に思ったりしてる。 まあintが妥当だろう。 しかし、_exit()に渡すのがintでwait()した時も一応exit statusはintだよな。 どこで上位ビット欠落させてんだ? >>69 OSの中だ。_exit() はシステムコールだし。 まあでも UNIX 系 OS じゃないならこれは違っているかも知れない。 ちょっとCの範疇を越えた話になるけど リターンコードが0--255ってどういう段階で決定されたんだろう。 DOSとUnixが同じ範囲のリターンコードを持ってるって、偶然とは考えがたいんだけども DOSの終了ステータスはUNIXの仕様をそのまま使用だと思う。 UNIXの方は、子プロセスを作って別の仕事をさせるって定型処理で、 「親プロセスで子プロセスの終了を待つ」ためのwait()系の関数が、 ・子プロセスが正常終了した場合は子プロセスの終了ステータス ・子プロセスが割込みで中断された場合は割込みの種類 という2つの情報を1個の返り値で戻すことと関係あるのじゃないかな。 1個の整数値を上位と下位のビット群に分けて別の情報として使うために それぞれの情報量を1バイトずつに制限した、と。 auto変数で char配列を可変長で動的に確保する方法は無いかな。 アセンブラレベルならスタックポインタを操作すれば可能だと思うが。 文字列処理の内部バッファとして入力に合わせたサイズを確保したいんだが、とりあえず今は固定長バッファとそれを超える場合は malloc でやってる。 >>75 gcc使うなりalloc()使うなりすればいい とりあえず vla c言語 で、ぐぐれ >>75 alloca 標準ではないけどほぼ標準扱い。(殆どの環境で使える) ただし realloca は無いので注意。 >>76-77 ありがとう、まさにぴったり。 言語レベルでの可変長配列は C11 から(オプションだけど)入ってるんだね。 90年代からメンテされてるコードだからそっちは使えなそうだけど、alloca を検討してみる。 >>79 環境わからんからなんとも言えんが組込みとかWindowsとか意外にスタックサイズがしょぼい環境あるから気を付けてね いやいや、C99からだよ。C11でオプションになった。答える方はそのくらい知っとけよ。 まあ入力が小さいのが予め分かってないと使いにくい機能だよ。 発端の >>75 の意図次第だが、free()しなくても安全に蒸発して欲しいとか、 ヒープの確保と解放の処理時間を嫌っての話ならgetline()はちょいと違うね。 文字数の予測が難しい入力を扱うにはとても便利なんだけど。 それにしても「上級者専用」って看板が架かってることを意識すると このスレッドは書き込みにくいな。気後れしちゃう。 動的確保が無難なんだよね。最後にfreeすればいいだけだし。 最近まで知らなかったんだけどscanfで%msで動的確保してくれるの便利だな。scanf自体はなかなか難しくて使いづらいが… >>81 あ、C99で入ったのをC11でオプショナルに格下げ(?)されたってことか。 処理系によっては厳しい実装なのかな。 やりたいことってのは文字列のエスケープを含んだ組み立てなんだけど、使用するエスケープ関数がありものでその仕様は入力文字列長に対して2倍以上の出力バッファを与えないといけないことになってる(出力サイズを指定して打ち切らせることができない)。 実際に2倍に膨らむことは稀だし、エスケープするのも文字列中の一部なので、自分の処理で最終的に出す出力結果は論理最大よりかなり小さくなる(自分の処理は指定された出力長で打ち切る)。 まず来ることが無い事態のために論理最大の配列を取っておくのもどうかと思い、いい方法があるかここで相談させてもらった。 しかし想定外のことが起きちゃった時にどうなるべきかを考えてどうするかを決めるから、もしかしたら動的配列の意義が無くなって別の方法にするかもしれない。 この処理自体は高頻度で呼ばれるから、高速で省メモリそして内部的な都合での打ち切りは極力避けた作りにしたいって感じ。 いろいろコメントありがとう あらかじめプールしておいて使いまわす 足りない時だけ臨時で増やす ReactOSというOSでフォントエンジンの改良を行っているが、 Google Chromeというブラウザでおかしくなる。 何故かEIPレジスタがゼロになって、初回例外が発生する。KDBという付属のデバッガでトレースしたが、 どこの関数で例外が発生しているかわからない。 たすけて。。。 >>85 > 動的確保が無難なんだよね。最後にfreeすればいいだけだし。 結局の所、結論としてはそうだね。 >>86 多分素直にmallocする関数をラップして使う方がいい。 仕様の動向自体は知らんが、 > 処理系によっては厳しい実装なのかな。 技術的にはこれはない。alloca相当(ポインタ相当)にすればいいだけなので。 ただ、間接アクセスになるから速度は落ちるし、 mallocに対しての利点は『自分で』freeしなくて済むことくらいしかない。 (『自分で』ソースに書かなくてよくなるだけで、速度上のメリットはない。 reallocaが無い為、最初のバッファ(=サイズが不明)はヒープ上に動的確保するしかなく、 自分で書いてなくてもどこかでfreeされてるだけだから。なら最初からgetlineでもいいし) ただ、そちらの実装は(おそらくバッファオーバラン対策で)一旦固定長バッファに取り込み、 その後alloca領域に対してのコピーか? ならまあ一応freeしなくていい速度上のメリットは残るが、 一般的にはスタックサイズ管理のコストが増える方が問題とされ、その実装はないとも思うが。 結局、allocaもイマイチなんだよ。だから標準にもなりきれない。 >>83 >>1 ,13読んで以下にどうぞ。 C言語なら俺に聞け https://mevius.5ch.net/test/read.cgi/tech/1534430162/ >>12 >>89 それがこのスレの正しい使い方かもしれんね。 ぱっと見て分かるものでもないけどさ。 allocaとか動的サイズ指定の配列はスタックだから基本的にはmallocするよりは速いよ。頑張っても同等。 繰り返して呼ぶなら差が出るかもね。 ああ、言い直しておくよ。 allocaとmallocなら、 確保:allocaの方が速い 使用:同速(ただし初回からキャッシュが当たる分allocaの方が速い) 解放:freeの必要が無い分、allocaの方が速い (ただし実装によっては上位でfreeしてるだけであり、同速) 管理:通常、ヒープサイズ>>>>>>スタックサイズの為、サイズ管理が必要 速度差が見えるような使い方が出来るのなら、ある意味大したもんだと思うよ。 スタックって確かスレッド毎に肥大化してくよね? で、肥大化してもスレッドが停止するまで縮小しない あってる? 関数から戻るとき(エピローグ)にスタックは縮小することがある。 実際のx86 CPUでスタックポインタを表しているのが、ESPレジスタの値。スタックサイズの増減はESPの書き換えに過ぎない。 流れのついでに聞いてみたいけど、malloc はスレッドセーフでしょ。 てことは内部で排他をかけてると思うけど、となるとマルチスレッド下で malloc や free を多発させるとパフォーマンス的に良くなかったりしないかな。 言ってなくてごめんだけど、今回の処理はマルチスレッドで動くんだ。 なのでバッファはスタック上に取れると都合がいいという事情もあるし、特別な初期化手順や終了手順も用意したくないから事前に malloc して使い回すってのもやりづらい。 ちなみに動作環境は x86 linux だから、alloca は SP をズラすだけの非常に高速な実装になってるんじゃないかと想像してるから、関心は高い。 でも、最悪でもスタック上に収まるバッファという前提にするなら最初から論理最大サイズ固定のバッファで良くね?なんて話もあるから、実際にどうするかはこれから検討。 glibcのmallocならサブarenaから獲得してくるから性能問題にはならないらしい >>99 サイズの問題がないのならallocaを使うことに問題はない。 mallocより遅くなる理由もないので。 ただ、普通に組めば分かるが、 > malloc や free を多発させる ってのがあり得ない。 仮にこれがallocaで有効に代用出来るとするなら、再帰下降パーサ等、「再帰」が必要になるが、 再帰下降パーサの場合はインミュータブルでよく、元の文書をオフセット付きで参考して終わりだ。 再びallocaすることはない。 同様に、ループでパースするのなら、ループの外でmallocして十分な領域を確保し、 そこに上書きで使うことになる。だからmalloc/freeは1回ずつで済む。 もう一度言うが、allocaはスタックだから、「再帰」しないと領域を追加出来ない。 この使い方は普通無いし、君もやって無いと思うよ。 でも、普通のmallocを全部allocaで代用しても、サイズ以外の問題は無いから、可能ならそれでもいい。 > alloca は SP をズラすだけの非常に高速な実装になってるんじゃないかと想像してるから これはその通り。 > 最初から論理最大サイズ固定のバッファで良くね? これもその通り。上記ループなら普通これで行く。 それがスタック上で問題になるサイズならmalloc/freeが1回ずつ必要になる。 だから君の今の実装>>75 もさほど悪いわけでもない。 今風の「実装を外に漏らさない」方針なら子関数でmalloc/freeやallocaすることになる。 おそらくそれで考えているのだと思うが、元々のCの思想はそれとは違い、 char* buff = (char*)malloc( ... ); // または char buff[2048]; 等 while (....) { parse_func(buff, .... ); } free(buff); として外側で確保し、それをparse_funcに渡す。 これにより、変数の寿命とスタックの動作を一致させ、freeし忘れもなくなる。 この方法だと、allocaで毎回「SP をズラすだけ」すらする必要なく、parse_func内は最速になる。 (allocaを毎回繰り返すよりも速い) あくまで処理系依存の話として… マルチスレッド固有の問題はほぼない気がしますね。malloc/freeは単に別の領域確保していくだけだしスタックの場合はスレッド生成時に確保すると。 で、まあmallocの実装はそこまで悪くないと思うけどスタックが速いし、さらに静的領域の方がちょっと命令数は少なくなるでしょう。 >>102-103 いろいろありがとう。材料は揃ってると思うので、最終的にどうするかはこれから決めるよ。 >>104 malloc は内部的にはヒープから領域を切り出してくるわけで、切り出したチャンクは恐らくリンクかなにかで管理してるはずでしょ。 これはプロセス単位で一式だから、マルチスレッドでよってたかってこの構造を更新するには排他は欠かせないと思うんだがどうなんだろ? 切り出されたメモリがマルチスレッド下でどうかという話ではなく。 >>100 の内容はちょっと分からないから調べてくるが、結局は誰かが排他してるんじゃないのかな。 >>96 実行環境依存だが、win/linuxならその認識で良い allocaは便利だがスタックの肥大化を加速させるので 環境によっては注意が必要 >>105 興味があれば https://youtu.be/0-vWT-t0UHg >>105 ナイーブなmallocの実装はそうです。中央集権的。同時に動くのは1個だけ。でもクリティカルセクションはそんなに広くないかも? まあ実装はいろいろあります。 >>106 すげーおもしろかった! マルチスレッドでどうやってるかについても分かったよ。 過渡期の性能を犠牲にして使っていくうちにいい状態に収束するようにしてるのね。 しかし malloc のコードが 5000行てw ただ、mmap すればメモリ管理のコストが小さい的な言いぶりはどうかなって気はするな、動画の中でもツッコミ入ってるっぽいけど。 結局カーネルだって何らかの形でアドレス空間の空きを検索するし、アドレス空間を割り当てる処理にしても排他はかけてるはずだから、それなりのコストはかかるし競合の問題もあるよね(ユーザーがやる処理よりも高効率だろうと思うけど)。 >>105 malloc の話を持ち出したのは、排他とかで結構遅いんじゃね?ってのが出発点なので、 排他はしてるって言うし mmap だからという説明じゃ解決って話でもないかなって感じ。 >>109 >>75 の頃の時点では K&R アロケータ程度の認識だったからマルチスレッド下ですげー遅そうな印象だったけど、さすがによく考えられているということは分かったよ。 ちなみにこれも読み始めてた。 https://www.valinux.co.jp/technologylibrary/document/linux/malloc0001/ >>106 の動画を見てだいぶ見通しがよくなった。 POSIX準拠を念頭にC言語を書かれている方に訊きたいのですが、機能検査マクロって実際どのように使われていますか #define _POSIX_C_SOURCE 200112L #define _XOPEN_SOURCE 600 ↑僕はこのくらいであればシステム互換性は担保されると思ってるんだけど 未だにPOSIX 1998にしか対応してないOSやコンパイラとかあるんですかね。 少なくとも_XOPEN_SOURCEが600以上じゃないと<math.h>においてM_PI定数が扱えないので 数式処理のプログラムをよく書く身としてはちょっと困るんですけども……。 好きな人が古い環境を使っているという事情でもなければ、単に無視すれば良いのでは >>114 CMakeで環境を判定して、マクロを定義するのが王道だと思う。 #ifdefでM_PIがないときだけ自分で定義するのが手っ取り早いと思うけど。 >>115 乙。てかreactOSって使い物になるのか? 無理してWindows使うよりは、素直にLinuxに逃げるのが順当だと思うが。 >>119 使い物にするのが、依頼人の要望だから、私はそれを、果たすのみ。。。 上級者ではないんだけど 経験豊富な人に訊きたいのでここに書きます https://git.musl-libc.org/cgit/musl/tree/include/stdlib.h ↑ここなどを見るとEXIT_SUCCESSは0で固定されています(OSによる場合分けがない)。 ということは少なくもMusl LibCプロジェクトは正常返り値を0と決めてかかっているということでしょう。 WindowsやUnix系のOSでは正常返り値は0ですが、ほかの値は絶対に考えなくていいのでしょうか みなさんはいままで正常返り値が0でない処理系(というかOS)を見たことなどありますか。 >>121 もしプロセス終了コードの具体的な数値(数値かどうか)が異なるシステムがあるなら、 正常終了を示す main() の戻り値 0 をその環境用の終了コードで成功の値にマップするのは そのシステム向けの処理系の仕事になる。 成功・失敗の区別以外に興味がない限りCのコードを書くプログラマが気にする必要はない と規格は想定している。 >>121 おまいWindowsでexe実行したとき必ず戻り値みてるか? BATとかあるけどさ ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.4 2024/05/19 Walang Kapalit ★ | Donguri System Team 5ちゃんねる