パスワードを忘れた? アカウント作成
266335 story
プログラミング

文字列操作は「C言語最大の欠点」か? 209

ストーリー by hylom
適材適所 部門より

あるAnonymous Coward 曰く、

ITProのコラム「記者の目」は複数の日経BPの雑誌記者が記事にはしにくい個人的な意見などを書いている人気コラムだが、最近「C言語最大の欠点」というタイトルのコラムが掲載された。

どうやらタレこみ子と同世代らしい記者は、C言語最大の欠点を文字列処理であると断じ、特にバッファーオーバーフローの回避のためのコードを書くのが大変なところと論じている。そして、C++でSTLのstringクラスを使うべし、と主張している。

タレこみ子の乏しい経験からいうと、バッファーオーバーフローの問題は確かに大きな問題だが、それは「文字列」処理で起きるというよりは、ネットワークから受信する「データ列」の処理で起きるケースのほうが多いように思う。STLについては詳しくないのだが、stringクラスは'\0'も含みうるデータ列を正しく処理できるのだろうか。

また、バッファーオーバーフローは問題として理解しやすく、コード中で対処しなければいけない場所も比較的容易に特定できるのに対し、整数オーバーフローの問題は、問題として理解が難しく、対処しなくてはいけない場所も格段に多いという点でより大きな欠点ではないかと感じている。

バッファーオーバーフローや整数オーバーフロー以外にもC言語には様々な欠点があると思うが、/.jp諸兄は何が「最大の欠点」であるとお考えであろうか。ご意見をお聞かせ願いたい。

また、あるAnonymous Coward 曰く、

日経ITproに「C言語の最大の欠点」なる記事が掲載されている。記事では、gets()を用いて入力文字列を受け取る例を挙げ、「C言語の最大の欠点は文字列の扱いが非常に面倒なこと」と主張している。そして、解決策として「C++」を使えとと述べ、C++のストリームとSTLのstringクラスを用い、同様の処理を簡単に記述できると例示している。

そのほか、「動的な配列として使えるvector」などをC++のメリットとして述べているのだが、これらはC++のメリットではなく、ライブラリのメリットのような気がするのだが……。

STLやストリームといった有用な標準ライブラリが用意されている、という点は確かにC++のメリットではあるが、Cでも同様のライブラリがないわけではない。これを持って「C言語には欠点がある」と述べてしまうと大きなフレームの元になってしまう気がしてならない。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by Anonymous Coward on 2010年10月22日 16時40分 (#1845694)
    コンパイルした結果の、CPUが実行するコードが想像できるような言語でないと、
    組み込みに代表されるようなハードウェアに根ざした用途(リアルタイムとかね)には向きません。
    裏でガベージコレクションが動くとか、問題外。
    そんなことやっている間にロケット落っこちまうよ。
    オシロスコープで実行時間計りながら開発してるんですから。

    用意されているライブラリが気に入らなければ使わなければ良いのです。
    たとえば、実際、最適化されたライブラリのコードでは、ブロック転送中などに割り込みが入らくなって
    しまう(あるプロセッサの例ですが、ライブラリ自体の実効効率は良い)のを回避する目的などで
    ライブラリ関数に相当する機能を自作するなど茶飯事です。
    ライブラリの機能制限してコンパクト化するとかね。(だから大抵の組み込み用コンパイラには
    ライブラリソースが付いてくる。逆についてこないと怖くて使えない)

    Cは、システム全体をコントロールできるという意味で余計な保護など要らないのです。
    (保護が必要なら、その部分までプログラマの責任で作る)
    nullポインタチェックなんかは「デバグの時には便利」ですが。

    ここからは、当該の相手に対して---

    OSや処理系(こっそり仕込まれる膨大なランタイムライブラリのサポートかあるいはJIT等の実行環境)
    におまかせらくちん!なプログラマが最近増えてますよね。
    だから、おまかせらくちん!なプログラマが、無意味にCに文句を言うのって、すごく
    筋違いのような気がします。
    黙ってインタープリタを使ってればいいのに。

    #もちろん、お任せらくちんでもすごいアプリを作ることはできるし、
    #要は適材適所。
  • 最大の欠点 (スコア:4, 興味深い)

    by manmos (29892) on 2010年10月22日 14時12分 (#1845544) 日記

    この記事の筆者は、20年以上Cでプログラムをしている様に書いているが、そのキャリアでも、この程度の理解しかしていない人間が生まれること(そして、その上、こんな文章をかいて恥を晒してしまうこと)が、最大の欠点では無いだろうか。

    まあ、「アセンブラレベルの記述ができる」ということは「アセンブラレベルのことを理解できる人間でしか使いこなせない」ということで、それは、そう簡単な事ではないのは自明の話ですな。

    • by j-ehara (31269) on 2010年10月22日 15時55分 (#1845642) 日記

      この記事の筆者は、20年以上Cでプログラムをしている様に書いているが、

      元記事を流し読みしましたが,この記事の筆者は

      Borland C++の大箱を買って帰った。

      から20年近く経過した,とは書いていますが,「(その後継続して)Cでプログラムを作っていた」とは書いていません。

      親コメント
  • by Anonymous Coward on 2010年10月22日 14時23分 (#1845552)

    Cはちょっと抽象度の高いマシン語だと教わったし、
    その理解は間違いではないと思う。
    欠点つーか、まさにそれが利点なんじゃねーの?

    #だから日本では、「使いこなせないのは修行が足りないからでは?」と考える。

    • by paprika (5024) on 2010年10月23日 19時07分 (#1846282) 日記

      私もCは構造化アセンブラだと割り切って使うので問題なし。
      で、C++なら、より安全な文字列も使えるし、やろうと思えば、ポインタを使ったトリックも使えるので、自由度が大きいです。

      #PHPで文字列操作をしようとしたときに悶絶したのは、修行が足りないからでしょうか?

      親コメント
    • マシン語だと言い張ってしまうと、「いやいや、あらゆるアーキテクチャから独立して仕様策定されている」とのツッコミが入る余地がめんどくさい気がします。

      もう、おおむねよく使われるコンピュータのアーキテクチャも固まってきたんだし、各アーキテクチャ向けに、アーキテクチャ依存しまくりの「ぶっちゃけこうなってますC」を策定しちゃえば良いと思います。x86-Cとかamd64-Cとか。

      例えば、ヌルポインターってなんなのか? [kouno.jp]の説明に過剰な慎重さが求められたりとかするのは、かなり無駄じゃないかと。もう「内部表現が『0x00000000』ポインタ」と決めてしまえば説明がすごく短くなるのに。

      他にも、「i=i++」はコンパイルエラー、とか「'\0'は0x00だ」とか、「実装はそうなってるけど、それはCの仕様ではなく、実装依存でたまたまそうなっているだけで・・・」という回りくどい説明を切って捨てられるように。
      親コメント
  • 次はメモリ管理へ (スコア:3, すばらしい洞察)

    by denchu (6847) on 2010年10月22日 14時58分 (#1845582)

    malloc したら free しなきゃいけないのが C 言語最大(!?)の欠点だ!
    という評論が載りそうな気がします。

    #バッファーオーバーフローが気になるなら gets じゃなくて fgets を使えばいいような?

  • by Stealth (5277) on 2010年10月22日 17時20分 (#1845733)

    そのほか、「動的な配列として使えるvector」などをC++のメリットとして述べているのだが、これらはC++のメリットではなく、ライブラリのメリットのような気がするのだが……。

    STL は C++ の言語仕様に含まれているライブラリーですから、そこまで含めての "C++ のメリット" というのは何ら問題ないように思います。
    Boost が使えるよ、であっても C++ のメリットとして挙げてもいいと思います。そこから「だから Python も使えるよ」とか言われると微妙な気分になれますが。

    個人的には「動的配列として使える vector」はメモリイメージ的に C と互換になる必要があるため、伸ばそうとした際に直後のメモリー領域が利用されている場合、再配置 + コピーが発生するなどの点を理解していないと残念な結果が待っていそうで怖いのですが。
    その辺りを意識せずに使うとパフォーマンス的にも色々残念な事になりやすいし (サイズ予約なしに追加しまくり→自動拡張呼ばれまくりのコスト)、うっかり vector にクラスのインスタンスとかをそのまま突っ込んでいて、コピー時にデストラクタが呼ばれてリソースの解放が行われて事故るとか。

    その意味でも、ある程度利用する予定の領域を先に確保しておくなどの点は重要になるのですが、なんというかそこまでの説明はなさそうかなぁ……日経だし。

  • 多くの場面においてC++のstringを使うべきだという点に関してはC++とくにSTL信者としては同意できる。
    BOFの件はCの文字列操作の貧弱性の例としてあまり適切ではないが、実際問題上、STLの使える環境下で、
    速度をはじめとするC++に対する数々の偏見を根拠に、C的な手法のみで文字列操作を行なおうとして、
    結果として車輪の再開発的になってしまったり、また各種バグの火種を作っている例は少なくないと思う。

    また、速度を考えなければ、iostreamのほうがコードのきれいさの観点から言っても良い場面は多いだろう。

    それとあと、STLやBoostは一様にC++の表現力に大きく下支えされた物で、ただ単に「ライブラリのせいだ、Cだって違やしない」という主張はどうかと思う。
    STLやBoostには、広く普及している事に伴う資源だってあるし容易にCで置換できる物ではない。

    ただ、元のBPの記事を書いた人間がそこまで考えて書いたとは思えないが。
    しかし、元の記者ほど酷い技術力じゃなくても、中・上級ぐらいの人で偏見のせいでC++が嫌いな人は多いし、
    このスラドの記事と付随するコメント含めて随所にその形跡が見てとれるように思う。

    #関係ないが、C/C++と書く奴は一様に信用できない。CとC++はモダンなPerlと古典的Perlの差以上に違う物だ。
    #一緒くたにしている連中が偉そうにのたまう事は必ずCの事と相場が決まっている。

  • by Dobon (7495) on 2010年10月23日 7時33分 (#1846075) 日記
    「プログラマは全知であり無謬である」これがC言語の前提です。
    従って、
    ・コンパイラはプログラマの記述をチェックすべきではない。
    ・コンパイラは単純かつ小さく保つべし。(余計な事をしてコンパイルを遅くするな)

    また、アプリケーションも、
    ・小さく単純に作れ。
    ・動作速度を優先せよ。移植性は犠牲にしてよい。(速さは七難隠す)

    この思想についてこれない人はC言語を使うべきではないと思います。
    # わたくしには厳しすぎるのでlintなどのツールを使いまくりですけど……
    --
    notice : I ignore an anonymous contribution.
  • 欠点 (スコア:2, おもしろおかしい)

    by Anonymous Coward on 2010年10月22日 13時59分 (#1845536)

    人前で話題にするのが恥ずかしい。

    #「はじめてのC」とか…

  • Cの生い立ち (スコア:2, すばらしい洞察)

    by Anonymous Coward on 2010年10月22日 14時05分 (#1845540)

    そもそもそういうのを一々自分でやらなきゃいけない=全部自分でできる、というのがCの売りじゃないの?

    • by ruto (17678) on 2010年10月22日 22時57分 (#1845946) 日記
      だとしたらprintfは高級すぎる気がします。

      # ところでprintfってCの標準関数の中で不自然に多機能でオーパーツ感があるけど、どういう歴史があるんだろう。
      親コメント
      • Re:Cの生い立ち (スコア:2, 参考になる)

        by bitterbeer_sweetwine (37563) on 2010年10月23日 3時29分 (#1846047)

        >だとしたらprintfは高級すぎる気がします。

        あれはfotranやcobolからの乗り換えを考えて(or対抗して?)、
        書式付き出力として付けられたものではないかな?

        fortranの場合、書式付き入力がちょっと弱そうなのだけど、
        高級すぎると思うのは、scanfの方ではないかと思う。

        親コメント
      • Re:Cの生い立ち (スコア:2, 参考になる)

        by tarna (10845) on 2010年10月23日 3時32分 (#1846049) 日記
        printfは便利すぎてやめられないです・・・

        私のcppファイルの先頭数行テンプレ
        個人的に使う簡単なフィルタだったり
        週末に数値計算やってみた、的なものがほとんどです。

        ----テンプレここから
        #include <stdio.h> // printfの為だけ。しかし絶対必要
        #include <string.h> // memcpyとかmemset使うかもしれないし
        #include <string> // 文字列操作はもちろんstring。最後に伝家の宝刀c_strでprintfに渡す!
        #include <vector> // stack,mapなんかも使いますね
        #include <algorithm> // 使わなくても書いておかないと日本人には覚えられないスペル
        #include <iostream> // coutなんて使いませんけど
        #include <fstream> // getlineは使いますよ、と
        using namespace std;
        ----ここまで

        cppファイルなのにclassなんて滅多に書かないです。
        printfは便利すぎてC++方式に移行できないです。
        「%08X」をC++流に書こうとしてキレましたw

        自分で組んで自分で使うプログラムだったら
        こんな風にCでSTLのみ利用するのが便利で楽ちんで気に入ってます。
        もちろんネット関連やチーム作業ではまずいかと思いますが。

        #倉庫番ソルバーを最終目標JavaScriptで、って組んでみたら
        #Cでもかなり厳しかったでござる、とほほぉ
        親コメント
  • by Anonymous Coward on 2010年10月22日 14時13分 (#1845545)

    STLについては詳しくないのだが、stringクラスは'\0'も含みうるデータ列を正しく処理できるのだろうか。

    それは使い方による。

    #include <iostream>
    #include <string>
    const char str[] = "a\0b";
    int main()
    {
      std::string a = str;
      std::cout << "size=" << a.size() << ", str=\"" << a << "\"" << std::endl;

      a.assign(str, sizeof(str)-1);
      std::cout << "size=" << a.size() << ", str=\"" << a << "\"" << std::endl;

      return 0;
    }

    結果:

    size=1, str="a"
    size=3, str="a^@b"

  • 検索語として不適切 (スコア:2, すばらしい洞察)

    by Anonymous Coward on 2010年10月22日 14時45分 (#1845567)

    Internet 普及前の言語だから仕方ないとはいえ、検索語として "C" は不適切なのが悩みの種です。
    "C++" までは時代的に仕方なかったと思いますが、これだけ Internet が普及してから登場した
    "C#" とか "GO" なんかは命名した奴を小一時間問いつめたいところです。

  • by ilonasive (6257) on 2010年10月22日 20時06分 (#1845852)
    strlcpy [wikipedia.org]なし。
  • ユーザー入力をgets系の1行入力処理で受ける発想自体、プロの文章とは思えない。
    これはC++に限った話ではない。PHPやRubyにしても、OSから1文字ずつ受け取るか、固定長バッファに一度転送してから入力値チェックするものでしょ。
  • 後方互換性 (スコア:1, 参考になる)

    by Anonymous Coward on 2010年10月22日 13時57分 (#1845533)

    C言語やバッファオーバーフロー時や整数オーバーフロー時の動作を未定義にせざるを得ないのは後方互換性(とスピード)のため。
    ちなみに未定義ということは当然エラーチェックしても何らC言語の仕様には違反しないので、Fail-Safe C [aist.go.jp]のような実装も可能。

  • 論理演算をやるつもりで、
    気が付かないで代入する件だとばっかり思っていました。
    • by Anonymous Coward on 2010年10月22日 14時58分 (#1845583)

      悪いことは言わないから、コンパイラの警告は最大に、警告内容はきちんと理解することを勧める。

      親コメント
    • ライティングソリッドコード [amazon.co.jp](カタカナだと何がなんやら。必殺技か?)に書いてあったと記憶してますが、
      if (1==n) {} で解決。

      両辺が変数の場合だけ気をつければ良いので、習得すると if のストレスが減る。

      親コメント
  • by Anonymous Coward on 2010年10月22日 14時19分 (#1845549)
    互換性がなくて良ければ、
    auto変数の配列を(リターンアドレス等に使う)メインのスタックではなく、別のスタックに置けば
    かなりの改善を得られると思うんです。

    別のスタックに対する操作はコンパイラが生成したコードでやるわけですから透過的に行えるし、
    そのオーバーヘッドは、いちいちヒープを使うよりも遥かに少なくそしてメモリリークのバグをやらかしにくい。

    まぁリターンアドレスを細工されることは防げても、他のデータにはみ出して上書きして特権的なフラグを操作する・・・なんてことには対処できませんが。
    • 「スタックは上のアドレスから使われる」のが諸悪の根源。
      下から使ってりゃオーバーフローしても被害はその関数の自動変数で収まる。

      親コメント
      • スタックがどちらに成長するかは処理系(とアーキテクチャ)依存です。とあるフリーのOSのライブラリを読んでるときに「MIPSでは下から上に成長するよ!」と書いてありました。上から下に向って成長しなければならない必然性はもはやありません。
        でも、いざ切り替えるとなると、いろいろうまく動かなくなるところがでてきそうですね。

        でも、切り替えたところでどのくらいメリットがあるでしょうか? overflowの攻撃を防ぐだけなら実質address space layout randomizationだけで100%防げているような感覚はあります。昨今バッファオーバーフローで攻撃が成立するのは明示的にaddress space layout randomizationをオフにしたプロセスだけではないでしょうか。

        親コメント
    • アイデアとしては面白いですが、その場合retaddrとデータ部はどこに置くことになりますか?

      現状、retaddrはpushすることにより、autoの変数はスタックポインタを減ずることで実現しているわけですが、現状のCPUのSPが1つである以上、retaddrとauto変数を別の場所に置くための仕掛けが必要となります。
      この場合の実装として考えられるのは、1. 2つのSPコンテキストを維持してリターン直前にretaddr用のaddrをSPにロードする 2. auto 変数をヒープを用いるよう変更してしまう という方法あたりになるかと思いますが、1ならコンテキスト(退避したレジスタの内容)をどこに配置するのか(結局固定した場所に配置してたらセキュリティ上の優位性は無いのではないか?)とか、2なら決定的に速度が低下しそうという懸念があります。

      親スレッドでは「いちいちヒープを使うよりも」と書いてあるので、1ということになりますかね。

      今思い付いたアイデアですが、いっそ、SPを使わないコード(全部inline展開)を生成するようにすればいいのでは? そうして出来上がったものがCと言えるかどうかはすごく微妙ですが。(苦笑)

      どんなものを想定されているのか、是非ここでお話いただければ刺激になります。

      親コメント
  • by fireboat (36349) on 2010年10月22日 14時21分 (#1845550) 日記
    バッファーオーバーフローは可変長データを扱う時に置きやすそうなので、そうすると、やっぱり代表格は文字列データということになるかと。

    最近は安全な文字列操作関数ライブラリ使ったり、コンパイル時に書式文字列と引数の不整合を指摘してくれるコンパイラ使うとか手段があるので、タレコミ後半の通りCそのものの問題ということでもないだろうけど。

    CVEの脆弱性リスト眺めてると、二重解放の脆弱性が頻度も多い割りに中々気づけない気がする。何か良い対処方法はありますか?
  • by firewheel (31280) on 2010年10月22日 14時30分 (#1845557)

    最大の欠点かどうかはさておき、問題にしている部分は正確にはC言語の配列操作
    あるいはポインタ操作じゃないの?

    ポインタが何でもできるから危険なのはたしかだけどね、それを無くしたらCじゃない。

typodupeerror

未知のハックに一心不乱に取り組んだ結果、私は自然の法則を変えてしまった -- あるハッカー

読み込み中...