蚘事「2016幎のCでの蚘述方法」に関する泚意



実際、これは歊噚であればアセンブラヌのように芋えたすが、Cでは、非垞に泚意する必芁がありたす



翻蚳者から

この出版物は、応答蚘事を「How to C in 2016」ずいうテキストに翻蚳したものです。 埌者の翻蚳は、金曜日に私によっお公開され 、いく぀かの堎所で、コミュニティからの混合反応を匕き起こしたした。 Habrのフレヌムワヌク内で既に問題の議論を維持するためのこの「答え」ぞのヒントは、 CodeRushナヌザヌから䞎えられたした。



以前に、 「Programming C in 2016」ずいう蚘事がオンラむンで公開され、倚くの有甚なヒントが掲茉されたしたが、残念ながらあたり良いアむデアではありたせんでした。 そのため、関連する点に぀いおコメントするこずにしたした。 私が新しい資料を準備しおいる間、誰かがCで仕事をするのは責任あるプログラマヌだけであり、他の蚀語は既存のスキルを改善する機䌚がもっずある無責任でなければならないこずに気付きたした。 その分野の専門家の秘密を理解したしょう。



デバッガヌを䜿甚する



ポむント1はおそらく無芖したすが、無駄になりたすが、執筆段階でコヌドの各行に察しおカヌネルレベルのデバッガヌを起動するこずです。 このツヌルの可胜性を特に耇雑な問題を解決するためだけに䜿甚するず、間違いを犯すこずになりたす。



぀たり、Visual Studio、Xcode、EclipseなどのIDEを䜿甚するずいうこずです。 この堎合、デバッグ機胜なしで゚ディタヌのみで䜜業する堎合、あなたは貧匱な仕事をしおいたす。 非垞に倚くの人がデバッグ機胜を持たない゚ディタヌでコヌドを曞くため、このニュアンスに蚀及したす。 そしお私も䟋倖ではありたせん。



これは、すべおの蚀語、特にCでプログラミングする堎合に重芁です。 メモリが砎損しおいる堎合は、゚ラヌを怜出するためにメモリの構造ず内容をダンプする必芁がありたす。 Xが奇劙な37653を出力するのはなぜですか printfスタむルのデバッグコマンドでは状況が明確になりたせんが、スタック䞊のhexdumpナヌティリティのデヌタを芋れば、特定の量の情報がどのように曞き蟌たれたかを確実に理解できたす。



コヌドのデバッグを忘れないでください



Cはメモリ保護を提䟛しないため、コヌドのこの郚分が砎損した郚分に関連しおいない堎合でも、ある堎所で発生した゚ラヌが別の堎所で発生する堎合がありたす。 これが、コヌドの問題のあるセクションをデバッグするこずが非垞に難しい理由です。 そのような堎合、倚くのプログラマヌが髪を匕き裂き、「修正できない」ず叫び、同僚に助けを求めたす。



同じ熊手を螏たないでください。 このような「解決䞍可胜な」タスクに䜕床か盎面するず、より良いコヌドを曞く方法を孊びたす。 これは、バグを迅速に怜出する自己テストコヌドです。たたは、曞き蟌みプロセス䞭に、境界線のケヌスをカバヌする、デヌタブロック党䜓の効果的な怜蚌ナヌティリティが䜿甚されたす。



コヌドを積極的に保護する



マネヌゞャヌがcatch...C ++でをプログラムのクラッシュを避けるためにどこにでも眮くこずにしたプロゞェクトにたたたた取り組みたした。 䟋倖やメモリ砎損のケヌスでさえ仮装しおマスクされ、プログラムは機胜し続けたした。 開発者は、コヌドの脆匱性を枛らしたように芋えたした。 圌らは、防埡的なプログラミングスタむルが優れた゜リュヌションであるず考えたした。



しかし、これは保護ではなく、愚かです。 埌で怜出するのがより難しい゚ラヌを隠すロゞックはどこにありたすか



異なる方法で行いたす。 攻撃の原則、぀たり最短時間で欠陥を特定するコヌドが必芁です。



このアプロヌチを珟実にする方法の1぀はassertです-二重のテストの前提。これにより、すべおを正しく行うこずができたす。 この関数は、メモリに害を䞎える前にバグを怜出したす。 私は真剣ですCの目立たない゚ラヌをデバッグするには、クラッシュを匕き起こす可胜性のある堎所にassertを挿入したす提案されたコマンドに倢䞭にならないでください。



コヌドに぀いお話しおいる堎合、「事前に攻撃する」ための最良の方法は、単䜓テストを䜿甚するこずです。 疑問がある堎合はすぐに、あいたいなパラメヌタヌをチェックする単䜓テストを䜜成しおください。 Cで䜜業しおいるずき、すべおが最初に蚈画どおりに進んだずきに混乱しやすく、その埌奇劙なこずが起こるずいう意芋がありたす。そのような堎合、適切なテストを甚意しおおくのも良いこずです。



コヌドは品質でなければなりたせん



この点に関しお、単䜓テスト、回垰テスト、さらにはファゞングのようなものが埐々に暙準になり぀぀ありたす。 オヌプン゜ヌスプロゞェクトがある堎合は、定性分析を実斜するためにmake testオプションを芁求するだけです。 これにより、高いカバレッゞで単䜓テストコヌドを実行できたす。 このアプロヌチは、倧芏暡なオヌプン゜ヌスプロゞェクトの暙準ず考えられおいたす。 私は冗談ではありたせん。高いコヌドカバレッゞでのナニットテストは、すべおの新しいプロゞェクトの出発点ずなるはずです。 これは、私の深刻なオヌプン゜ヌス開発のすべおで気づくでしょう。 ここで、私はすでに初期段階でモゞュヌル匏のテキストを曞き始め、次々ず曞き始めたすただし、私は怠け者なので、高いコヌドカバレッゞを誇るこずはできたせん。



AFLを介したファゞングは比范的新しい珟象ですが、このメカニズムをテストするこずにより、さたざたなオヌプン゜ヌスプロゞェクトのバグを識別するのにどれほど効果的かを理解できたす。 Cプログラミング蚀語は、倖郚からの解析に関しおはすべおの蚘録を砎りたす。 過去には、䞍適切なフォヌマットのファむルたたは無甚なネットワヌクパケットが原因で、゜フトりェアクラッシュが頻繁に発生しおいたした。 しかし、2016幎には、誰もそのようなナンセンスを容認したせん。 入力したデヌタに関係なく、プログラムがおそらく正しく動䜜するかどうかわからない堎合は、どこかで間違えられおいたす。



そしお、他の誰かが品質を倧事にすべきだず思われる堎合、あなたは再び間違っおいたす。



グロヌバル倉数を忘れる



私がC / C ++でオヌプン゜ヌスプロゞェクトに取り組んでいるずき、それは私が生きるこずを劚げるグロヌバル倉数です。 グロヌバル倉数の実際のホットベッドを䜜成したため、プロゞェクトをデバッグしおマルチスレッドパラメヌタを蚭定するこずは困難です。 それらの䜿甚を最小限に抑える-そしお、コヌドのリファクタリングははるかに効率的になりたす。



はい、デバッグ/ステヌタス登録システムに぀いお話しおいる堎合、グロヌバル倉数にアクセスする必芁さえありたす。他の堎合には、それらの存圚を忘れおください。





OOPを少し、関数型プログラミングずJavaを少し远加



圌らはしばしば「私は任意の蚀語でFORTRANでプログラムできたす」ず冗談を蚀い、プログラマヌはよく1぀たたは別の蚀語を他の目的に䜿甚し、䜿い慣れたツヌルに倉換しようずするこずを蚀及したす。 しかし、これは、どのようなプログラミング蚀語に遭遇しおも、愚かなプログラマヌは愚かなたたであるず蚀う方法です。 堎合によっおは、さたざたなプログラミングシステムに固有のナニバヌサルプロパティも正垞に機胜したす。



オブゞェクト指向プログラミングでは、構造の抂念が、情報の凊理に必芁なデヌタずメ゜ッドをどのように結合するかに関心がありたす。 struct Foobaの堎合、foo_xxxxに芁玄される䞀連の関数を䜜成したす。 コンストラクタfoo_create、デストラクタfoo_destroy、および構造を定矩する関数のホストは自由に䜿甚できたす。



最も重芁なのは、ヘッダヌファむルではなく、Cファむルでstruct Foobarを蚘述するこずです。 関数を公開したすが、ここでは構造の正確な圢匏を非衚瀺にするこずが望たしいです。 原則ずしお、構造ぞの盎接リンクが提䟛されたす。これは、ヘッダヌの゚クスポヌトがバむナリアプリケヌションむンタヌフェむスの互換性に圱響するラむブラリにずっお特に重芁です構造のサむズが倉曎されるため。 構造を゚クスポヌトする堎合は、バヌゞョンたたはサむズを最初のパラメヌタヌずしお指定したす。



いいえ、継承ずポリモヌフィズムに至るたで、OOPの詳现には觊れたせん。 それどころか、モゞュラヌプログラミングの利点に泚目しおください。原則ずしおOOPに䌌おいたす。



さらに、関数型プログラミングのいく぀かの優れたアむデア、぀たり「副䜜甚」を持たない固有の機胜のアむデアをキャンセルした人はいたせん。 これらは、゜ヌスデヌタず特定の出力情報を持぀ように蚭蚈されたオプションです。 アマチュア公挔はありたせん。 䜜成した機胜のほずんどは、このように芋えるはずです。 void foobarvoidのようなものを䜜成する堎合、 -トラブルを予期したす。



生掻を著しく耇雑にするツヌルのリストには、グロヌバル倉数も蚘茉されおいたす。 パフォヌマンスを䜎䞋させるシステムコヌルは同じカテゎリに分類されたす。 グロヌバル倉数は、int foobarstruct Xyz * pを介しお呌び出す構造䜓の奥深くに隠された倉数ず本質的に䌌おいたす。 その結果、必芁なパラメヌタヌを芋぀けるためにpの深さを掘り䞋げる必芁がありたす。 これがすべお衚面にある堎合はより簡単になり、リク゚ストはfoobar型p-> length、p-> socket-> status、p-> bbbで圢成されたす。 はい、この堎合、長くお厄介なパラメヌタのリストを操䜜する必芁がありたすが、耇雑な構造の代わりに、foobar関数は単玔な型に䟝存したす。



䞀郚には、プログラミングぞの同様の機胜的アプロヌチは定数の䜿甚によるものです。定数はポむンタヌをconstで指定するため、関数はそれらを倉曎できたせん。 しかし、結果がどこにあるか戻り倀ず非定数ポむンタヌ、および゜ヌスデヌタがどこにあるかは明らかです。



Cは䜎レベルシステムのプログラミング蚀語ですが、明らかに耇雑な堎合を陀いお、C蚀語を乱甚しないようにしおください。 特定のCの手法の代わりに、コヌドがCの条件に䌌おいおも、コヌドをより広い環境で実行可胜にするツヌルを䜿甚しお、JavaScript、Java、Cなどず統合できたす。



ポむンタヌ挔算なしで凊理する必芁がありたす。 はい、1980幎代にはコヌドの倧幅な高速化が可胜になりたしたが、1990幎代以降は、特に珟代の最適化コンパむラの堎合には圹に立たなくなりたした。 ポむンタヌ挔算は、コヌドの可読性を䜎䞋させるだけです。 オヌプン゜ヌスプロゞェクトがサむバヌ詐欺Hearbleed明らかにHear t bleed。、Shellshockなどにさらされるたびに、ポむンタヌ挔算を䜿甚しおコヌド内の理由を探しおください。 たたは、敎数むンデックス倉数を芋お、Javaでこれを蚘述しおいるかのように、察応するデヌタ配列を解析したす。



コヌドを蚘述するためのこのような理想的なアプロヌチは、構造/敎数のネットワヌクプロトコル/ファむル圢匏の解析を拒吊するこずも意味したす。 はい、ネットワヌクロギングのマニュアルでは、noths*short *pのようなものを䜿甚するこずを掚奚しおいたすが、このアドバむスは執筆時点では最も成功しおいたせんでしたが、今日では完党に䞍適切です。 Javaのように敎数パラメヌタヌを分析したすp [0] * 256 + p [1]。 詰め蟌たれた構造を衚面に固定するず、分析しやすくなるず思われたす-ありたせんでした。



安党でない機胜をブロックする



廃止されたstrcpyおよびsprintf関数の䜿甚を停止したす。 脆匱性を芋぀けた堎合、おそらくここにありたす。 さらに、このようなセットでは、䞊蚘の各関数を調べおバッファヌがいっぱいになっおいないこずを確認する必芁があるため、コヌドの監査ははるかに高䟡です。 バッファは倧䞈倫だず確信しおいるかもしれたせんが、長くお退屈なものがないかどうか確認する必芁がありたす。 いいえ、strlcpy/ strcpy_sおよびsnprintf/ sprintf_sを蚘述したす。



䞀般に、バッファオヌバヌフロヌず可倉サむズオヌバヌフロヌの意味を実際に認識する必芁がありたす。 OpenBSDでのreallocarrayの䜿甚方法、このオプションが倉数サむズのオヌバヌフロヌの問題を解決する理由を確認し、mallocの代わりにすべおのコヌドで䜿甚しおみおください。 必芁に応じお、元のreallocarrayをOpenBSDからコピヌし、プログラムのこの関数に固執したす。



特定のデヌタを入力するずきにコヌドが突然クラッシュする理由を知っおいたすか たぶんそれは圌らの安党だ。 ずころで、ハッカヌがコヌドをクラックするのはなぜですか あなたはすべおを正しく行いたす-そのような問題をもう心配する必芁はありたせん。



「2016幎のCを䜿甚したプログラミング」の蚘事では、どこでもcallocを䜿甚するためのヒントがありたした。 急いで埓わないでください。この堎合、倚くのプラットフォヌムで倉数サむズのオヌバヌフロヌが発生するためです。 たた、reallocなどの関数に慣れるず同時に、reallocarrayも䜿甚したす。



安党なコヌドを曞くための倚くのルヌルがありたすが、私が蚀ったこずに埓えば、この分野のほずんどの問題を解決できたす。 そしお、はい、たずえそれがロヌカルファむルたたはUSBポヌトであるずしおも、゜ヌスデヌタに぀いおは懐疑的です。



奇劙なコヌドでダりン



゜フトりェア開発を専門ずするすべおの䌁業に欠けおいるのは、仕事の埌の䌚議です。誰でも参加でき、統䞀されたスタむルの生成コヌドの提案を発声できたす。 その埌、誇瀺するすべおの人を解雇したす。 銬鹿げおいるね。



正しい「スタむル」ず呌ぶこずができる唯䞀のこずは、コヌドがむンタヌネットの同僚ず類䌌しおいるこずです。 これは、個人コヌドず通垞䜜業するオヌプン゜ヌスプロゞェクトの䞡方に適甚されたす。 Linux、BSD、WebKit、Gnuなどが提䟛する既存の有名なスタむルのいずれかを遞択するだけです。



他のプログラミング蚀語、特にPythonの利点のうち、非垞に少数の䞀般的に受け入れられおいるスタむルに泚目するこずができたすが、これはCに぀いおは蚀えたせん。たれで奇劙に芋えたす。 LibreSSLはBSD圢匏に倉換したした。 非垞に良い解決策Cのスタむルがあたりにも華やかすぎお叀くなったら、倚分それを䞀般的で銎染みのあるものに倉える時が来たのかもしれたせん。



あなたのコヌドでどれだけクヌルに芋えるかがわかるずすぐに、誰もがあなたず同じクヌルなものを䜿甚するず確信しおいたすか いいえ、それらを取り陀きたす、圌らは人々を悩たすだけです。 たたは、そのようなツヌルが重芁な堎合それが起こるこずもありたす、経隓を文曞化したす。



マルチコアプロセッサは未来です



プロセッサが高速になる可胜性は䜎いです。 ご芧のずおり、たすたす倚くのコアが衚瀺されおいたす。 いいえ、これはマルチスレッドコヌドを蚘述する必芁がないずいう意味ではありたせんが、おそらくそれらの将来に぀いお考える必芁がありたす。



ミュヌテックスずクリティカルセクションにはノヌず蚀いたす-コヌドを耇雑にするだけです。 はい、これにより補品の安党性は向䞊したすが、パフォヌマンスが䜎䞋したす。 したがっお、コヌドは2コアたたは3コアで飛ぶこずができたすが、それ以䞊あるず、プログラムの速床が䜎䞋し始めたす。 Cでのプログラミングのフレヌムワヌクで異なる数のコアのスケヌラビリティよりも重芁なのは、補品のセキュリティを適切なレベルに確保するこずだけです。



すぐに、さたざたなシステムの拡匵性に特化した巚倧な蚘事に粟通するこずになるず思いたすが、さっきも蚀ったように、組み蟌みの構造のデヌタ亀換のグロヌバル倉数ず隠された関数を取り陀きたす。 次に、コヌドずそのスケヌルをリファクタリングする必芁がある堎合、䜜業がはるかに簡単になりたす。



成功/倱敗のtrue / falseを忘れる



蚘事「2016幎のCによるプログラミング」は、成功は垞に真実であるず述べおいたす。 ナンセンス。 真実は真実であり、成功は成功です。 それらの間に等号を入れないでください。 結果が成功した堎合に0を取埗し、関数が倱敗した堎合に別の倀を取埗するには、耇雑なコヌドを蚘述する必芁がありたす。



はい、これは非垞にナンセンスです。暙準はありたせん。 「2016幎のCでのプログラミング」からの悪いアドバむスに耳を傟ける代わりに、このドキュメントが「奇劙なコヌドのダりン」ずいう玠晎らしい仕事をどのように行っおいるかを芋おください。 著者は、同じこずを他の人に教えおいたら、自分のコヌドを台無しにせずに良い䟋を蚭定したなら、問題の痕跡はないず信じおいたす。 玠朎です。 プログラマはこの暙準を決しお受け入れたせん。 あなたのコヌドはあいたいさに満ちた䞖界で生き残る必芁がありたす。最初ず反察の倀にもかかわらず、trueず0は成功を意味したす。 暙準の䜜成は、SUCCESSおよびFAILUREむンゞケヌタの明確な決定によっおのみ可胜です。



コヌドが次のようになっおいる堎合



if (foobar(x,y)) { ...; } else { ...; }  ,      ,   success,   failure.     .   : if (foobar(x,y) == Success) { ...; } else { ...; }
      
      







敎数に぀いお少し



「Programming with C in 2016」ずいう蚘事の著者は、叀兞的なintたたはunsignedを䜿甚する理由はないず䞻匵しおおり、代わりにint32_tおよびuint32_tを参照する方が良いず䞻匵しおいたす。 ナンセンス intおよびlongコマンドは、ほずんどのラむブラリ関数で初期デヌタを入力するために䞀般的に受け入れられ、同じ皮類の異なるタむプを芁求する堎合でも、䞀皮のタむプ保護を提䟛し、ナヌザヌに通知したす。



正盎なずころ、64ビットおよび32ビットシステムを含め、敎数の誀った倀を蚭定するこずは難しくありたせん。 はい、intを䜿甚しおポむンタヌを制埡するず、64ビットコヌドが砎損したす代わりにintptr_t、ptrdiff_tたたはsize_tを蚘述したすが、実際にこれがどのように発生するかは信じられたせん。 最初の4ギガバむトにmmapを蚭定するだけで、ロヌド時にこれらのペヌゞが無効であるこずに泚意しお、ナニット/回垰テストを実行するず、問題をすばやく解決できたす。 そしお、それをもっず良くする方法を説明する必芁はありたせん。

コヌドで最も厄介なのは、プログラマヌが敎数型の再定矩を急いでいるずいうこずです。 それを瞛りたす。 u32はコヌドに特別な魅力を䞎えるこずを理解しおいたすが、この芁玠は私を驚かせたす。 しかし、コヌドを読む必芁がありたす。 uint32_tやunsigned intなどの暙準的なものに眮き換えおください。 そしお、恐ろしいこずに、filesizeのような敎数型を任意に䜜成するだけで十分です。 遞択した党䜓に新しい意味を䞎えたいず思っおいたすが、Cプログラミングが「䜎」になるように蚭蚈されおいるこずを忘れないでください。したがっお、プログラマヌはそのような真珠をチェックする過皋で死にたす。



静的および動的分析を䜿甚する



以前のC仕様は「譊告レベル」ず「リント」オプションに芁玄されおいたしたが、今日では「静的分析」が代わりになりたした。 コンパむラはClangを䜿甚しお、ナヌザヌに新しいメッセヌゞをたすたす喜んでいたす。gccはこれに远い぀いおいたす。 たた、倚くは最新ではありたせんが、MicrosoftコンパむラヌはClangレベルで静的分析も提䟛したす。 メカニズムはClang自䜓ず同じですが、Clang分析のXcodeの芖芚化機胜は本圓に印象的です。



ただし、これは䞀般的な静的分析にすぎたせん。 しかし、静的分析をより高いレベルに匕き䞊げるセキュリティツヌルは他にもたくさんありたす-Coverity、Veracode、HP Fortify。



これには「誀った寛容」の原則がないわけではありたせんが、これは間違った定矩です。 このようなコマンドをコヌドに蚘述するこずで、「クリヌンアップ」できたす。぀たり、より信頌性の高い結果が埗られたす。 蚀い換えれば、ポップアップスキヌムを䜿甚するず、コヌドを完党なものにし、䞍芁な芁玠を削陀できたす。 静的アナラむザヌの厳栌な監督の䞋でコヌドを蚘述するず、プログラミングスキルが向䞊したす。



これらの恐ろしい䞭毒



数幎にわたる䌁業の存圚の埌、プロゞェクトは珟圚のシステムにのみ衚瀺されたす。 そしお、それは膚倧な数のシステム化されおいない䟝存関係が蓄積するためです。 私が働いおいた䌚瀟の1぀で、圌らは材料を競合他瀟ず共有しなければならないず冗談を蚀いたした。



そしお、非垞に正圓な理由により、この慣行は氞久に修正されたす。 別の䌚瀟は、チヌムが異なるコンパむラを䜿甚するずきに発生する統合の問題を回避するために、コンパむラのバヌゞョンを統䞀するこずを提案したした。 しかし、このアプロヌチは比范的小さな問題を解決し、それははるかに深刻な問題に眮き換えられおいたす。 統合に関する問題を排陀し、䞀皮のコヌド再線成を実行したす。



たた、オヌプン゜ヌスには特定の困難が䌎いたす。 䟝存関係が完党に文曞化されるこずはめったにありたせんが、それらに十分な欠陥がありたす。 その結果、ほずんどの堎合、必芁なコヌドを奇跡的にコンパむルするために、同じ䟝存関係の2぀の互換性のないバヌゞョンをむンストヌルするのに倚くの時間を費やしたす。



䟝存関係が少ないほど、コヌドの人気が高たりたす。 これを行うには、次のもののみが必芁です。







Cの未定矩の動䜜に察凊する



ほずんどの堎合、C蚀語がどのように機胜するかをよく理解しおいないため、匏x + 1 <xを怜蚎しおください。 この堎合の実際の結果は5です。たた、この倉数が最倧敎数倀を取埗し、1を远加するず、Cがxのアクションを指定しないため、オヌバヌフロヌが発生したす。 倚くのコンパむラは、他のプログラミング蚀語Javaなどず同様に、このような珟象をバむナリオヌバヌフロヌずしお識別したす。 しかし、ご存じのずおり、䞀郚のコンパむラヌはこの皮の状況を自動的に䞍可胜ず分類し、関連するすべおのコヌドを単玔に削陀したす。



したがっお、Cのコンパむラの珟圚のバヌゞョンの動䜜からではなく、Cの他のコンパむラがコヌドにどのように反応するかを分析するこずにより、远加の偎面を掘り䞋げる必芁がありたす。



おわりに



責任を負うこずに慣れおいない限り、Cでプログラミングを行わないでください。 バッファオヌバヌフロヌ、可倉サむズオヌバヌフロヌ、スレッド同期、未定矩の動䜜などの抂念を培底的に凊理するこずが重芁です。責任には、バグの早期怜出甚に蚭蚈された高品質コヌドの䜜成が含たれたす。 Cプログラミングが2016幎に開発するのは、これらの条件です。



All Articles