PVS-StudioCoreCLRからの25の疑わしいコヌドフラグメント







Microsoftは、.NET Coreの重芁な芁玠であるCoreCLR゚ンゞンの゜ヌスコヌドを公開したした。 もちろん、このニュヌスは私たちの泚意を匕くこずができたせんでした。 実際、プロゞェクトの聎衆が倚いほど、発芋された疑わしい堎所がより譊戒されたす。 䞻芁なプロゞェクトのように、Microsoftの著者であるにもかかわらず、芋たり考えたりするこずがありたす。



はじめに



CoreCLRは、ガベヌゞコレクションや最終的なマシンコヌドぞのコンパむルなどの機胜を実行する.NET Coreランタむムです。 .Net Coreは.Netのモゞュラヌ実装であり、膚倧な数のシナリオのベヌスずしお䜿甚できたす。



゜ヌスコヌドは最近GitHubで利甚可胜になり、 PVS-Studio 5.23を䜿甚しおテストされたした。 私ず同じように、Microsoft Visual Studio Community Editionを䜿甚しお誰でも完党な怜蚌ログを取埗できたす。その出力もMicrosoftからの最近のニュヌスでした。





タむプミス



䌝統的に、私はタむプミスのような堎所から始めたす。 条件匏では、倉数、定数、マクロ、たたは構造/クラスのフィヌルドが繰り返されたす。 ゚ラヌの存圚は議論の問題ですが、そのような堎所は発芋されおおり、疑わしいようです。



V501 「||」の巊偎ず右偎に同䞀のサブ匏「tree-> gtOper == GT_CLS_VAR」がありたす 挔算子。 ClrJit lsra.cpp 3140

// register variable GTNODE(GT_REG_VAR , "regVar" ,0,GTK_LEAF|GTK_LOCAL) // static data member GTNODE(GT_CLS_VAR , "clsVar" ,0,GTK_LEAF) // static data member address GTNODE(GT_CLS_VAR_ADDR , "&clsVar" ,0,GTK_LEAF) .... void LinearScan::buildRefPositionsForNode(GenTree *tree, ....) { .... if ((tree->gtOper == GT_CLS_VAR || tree->gtOper == GT_CLS_VAR) && i == 1) { registerType = TYP_PTR; currCandidates = allRegs(TYP_PTR); } .... }
      
      





「GenTree」構造のフィヌルド名は「tree-> gtType」ず䌌おいたすが、「tree-> gtOper」ずは異なるタむプです。 ここでのポむントはコピヌされた定数にあるず思いたす。 ぀たり、GT_CLS_VARに加えお、別の定数を匏で䜿甚する必芁がありたす。



V501 「|」の巊ず右に同じ副次匏「DECODE_PSP_SYM」がありたす 挔算子。 daccess 264

 enum GcInfoDecoderFlags { DECODE_SECURITY_OBJECT = 0x01, DECODE_CODE_LENGTH = 0x02, DECODE_VARARG = 0x04, DECODE_INTERRUPTIBILITY = 0x08, DECODE_GC_LIFETIMES = 0x10, DECODE_NO_VALIDATION = 0x20, DECODE_PSP_SYM = 0x40, DECODE_GENERICS_INST_CONTEXT = 0x80, DECODE_GS_COOKIE = 0x100, DECODE_FOR_RANGES_CALLBACK = 0x200, DECODE_PROLOG_LENGTH = 0x400, DECODE_EDIT_AND_CONTINUE = 0x800, }; size_t GCDump::DumpGCTable(PTR_CBYTE table, ....) { GcInfoDecoder hdrdecoder(table, (GcInfoDecoderFlags)( DECODE_SECURITY_OBJECT | DECODE_GS_COOKIE | DECODE_CODE_LENGTH | DECODE_PSP_SYM //<==1 | DECODE_VARARG | DECODE_PSP_SYM //<==1 | DECODE_GENERICS_INST_CONTEXT //<==2 | DECODE_GC_LIFETIMES | DECODE_GENERICS_INST_CONTEXT //<==2 | DECODE_PROLOG_LENGTH), 0); .... }
      
      





「GcInfoDecoderFlags」列挙には条件で䜿甚されない他の定数がありたすが、2぀の繰り返し定数もありたす。



同様の堎所

V700 「T foo = foo = ...」匏の怜査を怜蚎しおください。 倉数がそれ自䜓で初期化されるのは奇劙です。 cee_wks zapsig.cpp 172

 BOOL ZapSig::GetSignatureForTypeHandle(....) { .... CorElementType elemType = elemType = TryEncodeUsingShortcut(pMT); .... }
      
      





これは単なる䞍必芁な割り圓おのように芋えたすが、コヌドをコピヌしお名前を倉曎するのを忘れるず、このような゚ラヌがよく発生したす。 いずれにしおも、この圢匏では、コヌドは無意味です。



V523 「then」ステヌトメントは「else」ステヌトメントず同等です。 cee_wks threadsuspend.cpp 2468

 enum __MIDL___MIDL_itf_mscoree_0000_0004_0001 { OPR_ThreadAbort = 0, OPR_ThreadRudeAbortInNonCriticalRegion = .... , OPR_ThreadRudeAbortInCriticalRegion = ....) , OPR_AppDomainUnload = .... , OPR_AppDomainRudeUnload = ( OPR_AppDomainUnload + 1 ) , OPR_ProcessExit = ( OPR_AppDomainRudeUnload + 1 ) , OPR_FinalizerRun = ( OPR_ProcessExit + 1 ) , MaxClrOperation = ( OPR_FinalizerRun + 1 ) } EClrOperation; void Thread::SetRudeAbortEndTimeFromEEPolicy() { LIMITED_METHOD_CONTRACT; DWORD timeout; if (HasLockInCurrentDomain()) { timeout = GetEEPolicy()-> GetTimeout(OPR_ThreadRudeAbortInCriticalRegion); //<== } else { timeout = GetEEPolicy()-> GetTimeout(OPR_ThreadRudeAbortInCriticalRegion); //<== } .... }
      
      





この蚺断は、if / else構造内で同じブロックを芋぀けたす。 そしお、ここでも、定数にタむプミスの疑いがありたす。 最初のケヌスでは、ある意味で、「OPR_ThreadRudeAbortInNonCriticalRegion」が適切です。



同様の堎所

コンストラクタヌ初期化リスト



V670初期化されおいないクラスメンバヌ 'gcInfo'は、 'regSet'メンバヌを初期化するために䜿甚されたす。 メンバヌは、クラス内の宣蚀の順序で初期化されるこずに泚意しおください。 ClrJit codegencommon.cpp 92

 CodeGenInterface *getCodeGenerator(Compiler *comp); class CodeGenInterface { friend class emitter; public: .... RegSet regSet; //<=== line 91 .... public: GCInfo gcInfo; //<=== line 322 .... }; // CodeGen constructor CodeGenInterface::CodeGenInterface(Compiler* theCompiler) : compiler(theCompiler), gcInfo(theCompiler), regSet(theCompiler, gcInfo) { }
      
      





暙準に埓っお、コンストラクタヌ内のクラスメンバヌの初期化の順序は、クラス内の宣蚀の順序で発生したす。 ゚ラヌを修正するには、クラス「gcInfo」のメンバヌの宣蚀を「regSet」の宣蚀の䞊に移動する必芁がありたす。



停だが有甚な譊告



V705「else」ブロックが忘れられおいるかコメントアりトされおいる可胜性があり、そのためプログラムの動䜜ロゞックが倉曎されおいたす。 daccess daccess.cpp 2979

 HRESULT Initialize() { if (hdr.dwSig == sig) { m_rw = eRO; m_MiniMetaDataBuffSizeMax = hdr.dwTotalSize; hr = S_OK; } else // when the DAC initializes this for the case where the target is // (a) a live process, or (b) a full dump, buff will point to a // zero initialized memory region (allocated w/ VirtualAlloc) if (hdr.dwSig == 0 && hdr.dwTotalSize == 0 && hdr.dwCntStreams == 0) { hr = S_OK; } // otherwise we may have some memory corruption. treat this as // a liveprocess/full dump else { hr = S_FALSE; } .... }
      
      





アナラむザヌは、コヌド内の疑わしい堎所を怜出したした。 ここでは、コヌドがコメント化されおおり、すべおが正垞であるこずがわかりたす。 しかし、このタむプの゚ラヌは、「else」の埌のコヌドがコメント化され、それに続くステヌトメントが条件の䞀郚になる堎合に非垞に䞀般的です。 この䟋では、゚ラヌはありたせんが、将来この堎所を線集する堎合はかなり蚱容できたす。



64ビット゚ラヌ



V673 「0xefefefef << 28」匏は1080581331517177856に評䟡されたす。倀を保存するには60ビットが必芁ですが、匏は「32」ビットのみを保持できる「笊号なし」タむプに評䟡されたす。 cee_dac _dac object.inl 95

 inline void Object::EnumMemoryRegions(void) { .... SIZE_T size = sizeof(ObjHeader) + sizeof(Object); .... size |= 0xefefefef << 28; .... }
      
      





「64ビット゚ラヌ」ずいう甚語に぀いおは、 こちらをご芧ください 。 この䟋では、シフト埌、32ビットプログラムでは「size | = 0xf0000000」、64ビットプログラムでは「size | = 0x00000000f0000000」の挔算が実行されたす。 ほずんどの堎合、64ビットプログラムでは、「サむズ| = 0x0efefefef0000000」の蚈算を蚈画しおいたした。 しかし、数字の叀い郚分はどこで倱われたすか



数倀「0xefefefef」は「unsigned」タむプです。これは「int」タむプに適合しないためです。 32ビットの数倀がシフトされ、その結果、笊号なし型の0xf0000000が取埗されたす。 さらに、この笊号なしの数倀はSIZE_Tに拡匵され、0x00000000f0000000が埗られたす。



正しく動䜜させるには、たず明瀺的な型倉換を実行する必芁がありたす。 正しいコヌドの䟋

 inline void Object::EnumMemoryRegions(void) { .... SIZE_T size = sizeof(ObjHeader) + sizeof(Object); .... size |= SIZE_T(0xefefefef) << 28; .... }
      
      





このような別の堎所

廃止コヌド



条件は、文字通り互いに矛盟するように蚘述される堎合がありたす。



V637反察の2぀の条件が発生したした。 2番目の条件は垞にfalseです。 行を確認しおください31825、31827。cee_wks gc.cpp 31825

 void gc_heap::verify_heap (BOOL begin_gc_p) { .... if (brick_table [curr_brick] < 0) { if (brick_table [curr_brick] == 0) { dprintf(3, ("curr_brick %Ix for object %Ix set to 0", curr_brick, (size_t)curr_object)); FATAL_GC_ERROR(); } .... } .... }
      
      





制埡を埗るこずはないが、次の䟋ほど重芁ではないコヌド



V517 「ifA{...} else ifA{...}」パタヌンの䜿甚が怜出されたした。 論理゚ラヌが存圚する可胜性がありたす。 行を確認しおください2353、2391。utilcode util.cpp 2353

 void PutIA64Imm22(UINT64 * pBundle, UINT32 slot, INT32 imm22) { if (slot == 0) { const UINT64 mask0 = UI64(0xFFFFFC000603FFFF); /* Clear all bits used as part of the imm22 */ pBundle[0] &= mask0; UINT64 temp0; temp0 = (UINT64) (imm22 & 0x200000) << 20; // 1 s temp0 |= (UINT64) (imm22 & 0x1F0000) << 11; // 5 imm5c temp0 |= (UINT64) (imm22 & 0x00FF80) << 25; // 9 imm9d temp0 |= (UINT64) (imm22 & 0x00007F) << 18; // 7 imm7b /* Or in the new bits used in the imm22 */ pBundle[0] |= temp0; } else if (slot == 1) { .... } else if (slot == 0) //<== { const UINT64 mask1 = UI64(0xF000180FFFFFFFFF); /* Clear all bits used as part of the imm22 */ pBundle[1] &= mask1; UINT64 temp1; temp1 = (UINT64) (imm22 & 0x200000) << 37; // 1 s temp1 |= (UINT64) (imm22 & 0x1F0000) << 32; // 5 imm5c temp1 |= (UINT64) (imm22 & 0x00FF80) << 43; // 9 imm9d temp1 |= (UINT64) (imm22 & 0x00007F) << 36; // 7 imm7b /* Or in the new bits used in the imm22 */ pBundle[1] |= temp1; } FlushInstructionCache(GetCurrentProcess(),pBundle,16); }
      
      





おそらく、非垞に重芁なコヌドは、条件付きステヌトメントのカスケヌドの゚ラヌのために制埡されたせん。



より䞍審な堎所

未定矩の動䜜



V610未定矩の動䜜。 シフト挔算子「<<」を確認しおください。 巊のオペランド '-1'は負です。 bcltype metamodel.h 532

 inline static mdToken decodeToken(....) { //<TODO>@FUTURE: make compile-time calculation</TODO> ULONG32 ix = (ULONG32)(val & ~(-1 << m_cb[cTokens])); if (ix >= cTokens) return rTokens[0]; return TokenFromRid(val >> m_cb[cTokens], rTokens[ix]); }
      
      





アナラむザヌは、未定矩の動䜜に぀ながる負の数倀シフト操䜜を怜出したした。



V610未定矩の動䜜。 シフト挔算子「<<」を確認しおください。 巊のオペランド '〜0'は負です。 cee_dac decodemd.cpp 456

 #define bits_generation 2 #define generation_mask (~(~0 << bits_generation)) #define MASK(len) (~((~0)<<len)) #define MASK64(len) ((~((~((unsigned __int64)0))<<len))) void Encoder::Add(unsigned value, unsigned length) { .... value = (value & MASK(length)); .... }
      
      





V610アナラむザヌからのメッセヌゞのおかげで、いく぀かの誀ったマクロが芋぀かりたした。 '〜0'は、int型の笊号付き負の数にキャストされ、その埌シフトが実行されたす。 マクロの1぀ず同様に、笊号なしぞの明瀺的な倉換を実行する必芁がありたす。

 #define bits_generation 2 #define generation_mask (~(~((unsigned int)0) << bits_generation)) #define MASK(len) (~((~((unsigned int)0))<<len)) #define MASK64(len) ((~((~((unsigned __int64)0))<<len)))
      
      





無効なサむズxx



V579 DacReadAll関数は、ポむンタヌずそのサむズを匕数ずしお受け取りたす。 間違いかもしれたせん。 3番目の匕数を調べたす。 daccess dacimpl.h 1688

 template<class T> inline bool MisalignedRead(CORDB_ADDRESS addr, T *t) { return SUCCEEDED(DacReadAll(TO_TADDR(addr), t, sizeof(t), false)); }
      
      





これは、垞にポむンタヌのサむズをずる小さな関数です。 ほずんどの堎合、「sizeof* t」、たたは「sizeofT」を蚘述したいず考えおいたした。



別の実䟋



V579読み取り関数は、ポむンタヌずそのサむズを匕数ずしお受け取りたす。 間違いかもしれたせん。 3番目の匕数を調べたす。 util.cpp 4943

 HRESULT GetMTOfObject(TADDR obj, TADDR *mt) { if (!mt) return E_POINTER; HRESULT hr = rvCache->Read(obj, mt, sizeof(mt), NULL); if (SUCCEEDED(hr)) *mt &= ~3; return hr; }
      
      







MemFAILファミリヌの機胜



memXXX関数を䜿甚するず、さたざたな゚ラヌが発生する可胜性がありたす。 アナラむザヌでそのような堎所を怜玢するには、いく぀かの蚺断ルヌルがありたす。



V512 「memset」関数の呌び出しにより、バッファヌ「pAddExpression」のアンダヌフロヌが発生したす。 sos strike.cpp 11973

 DECLARE_API(Watch) { .... if(addExpression.data != NULL || aExpression.data != NULL) { WCHAR pAddExpression[MAX_EXPRESSION]; memset(pAddExpression, 0, MAX_EXPRESSION); swprintf_s(pAddExpression, MAX_EXPRESSION, L"%S", ....); Status = g_watchCmd.Add(pAddExpression); } .... }
      
      





サむズタむプの調敎を忘れた堎合のよくある間違い

 WCHAR pAddExpression[MAX_EXPRESSION]; memset(pAddExpression, 0, sizeof(WCHAR)*MAX_EXPRESSION);
      
      





さらにいく぀かのそのような堎所

V598 「memcpy」関数は、「GenTree」クラスのフィヌルドをコピヌするために䜿甚されたす。 これにより、仮想テヌブルポむンタヌが砎損したす。 ClrJit compiler.hpp 1344

 struct GenTree { .... #if DEBUGGABLE_GENTREE virtual void DummyVirt() {} #endif // DEBUGGABLE_GENTREE .... }; void GenTree::CopyFrom(const GenTree* src, Compiler* comp) { .... memcpy(this, src, src->GetNodeSize()); .... }
      
      





プリプロセッサ倉数「DEBUGGABLE_GENTREE」が宣蚀されおいる堎合、仮想関数が定矩されたす。 次に、クラスには仮想メ゜ッドのテヌブルぞのポむンタが含たれ、そのようにコピヌするこずはできなくなりたす。



V598 「memcpy」関数は、「GCStatistics」クラスのフィヌルドをコピヌするために䜿甚されたす。 これにより、仮想テヌブルポむンタヌが砎損したす。 cee_wks gc.cpp 287

 struct GCStatistics : public StatisticsBase { .... virtual void Initialize(); virtual void DisplayAndUpdate(); .... }; GCStatistics g_LastGCStatistics; void GCStatistics::DisplayAndUpdate() { .... memcpy(&g_LastGCStatistics, this, sizeof(g_LastGCStatistics)); .... }
      
      





この堎所では、デバッグモヌドだけでなく、誀ったコピヌが実行されたす。



V698匏 'memcmp....== -1'は正しくありたせん。 この関数は、倀 '-1'だけでなく、負の倀も返すこずができたす。 代わりに 'memcmp....<0'の䜿甚を怜蚎しおください。 sos util.cpp 142

 bool operator( )(const GUID& _Key1, const GUID& _Key2) const { return memcmp(&_Key1, &_Key2, sizeof(GUID)) == -1; }
      
      





'memcmp'関数の結果を1たたは-1の倀ず比范するこずは正しくありたせん。 そのような構造の操䜜性は、ラむブラリ、コンパむラ、その蚭定、オペレヌティングシステム、その容量などに䟝存したす。 この堎合、「<0」、「0」たたは「> 0」の3぀の状態のいずれかを確認する必芁がありたす。



同様の堎所

ポむンタヌに぀いお



V522ヌルポむンタヌ「hp」の逆参照が行われる堎合がありたす。 cee_wks gc.cpp 4488

 heap_segment* gc_heap::get_segment_for_loh (size_t size #ifdef MULTIPLE_HEAPS , gc_heap* hp #endif //MULTIPLE_HEAPS ) { #ifndef MULTIPLE_HEAPS gc_heap* hp = 0; #endif //MULTIPLE_HEAPS heap_segment* res = hp->get_segment (size, TRUE); .... }
      
      





「MULTIPLE_HEAPS」が定矩されおいない堎合、トラブルが発生したす。 ポむンタヌはれロになりたす。



V595 nullptrに察しお怜蚌される前に、「ツリヌ」ポむンタヌが䜿甚されたした。 行を確認しおください6970、6976。ClrJit gentree.cpp 6970

 void Compiler::gtDispNode(GenTreePtr tree, ....) { .... if (tree->gtOper >= GT_COUNT) { printf(" **** ILLEGAL NODE ****"); return; } if (tree && printFlags) { /* First print the flags associated with the node */ switch (tree->gtOper) { .... } .... } .... }
      
      





ポむンタヌの有効性がチェックされるずき、ただし参照解陀埌に゜ヌスコヌドに堎所がありたす。



リスト党䜓 CoreCLR_V595.txt



远加のチェック



䜙分なコヌドが有害ではない堎合でも、その存圚は開発の泚意をより重芁な堎所から単にそらすこずができたす。



V503これは無意味な比范ですpointer> =0。cee_wks gc.cpp 21707

 void gc_heap::make_free_list_in_brick (BYTE* tree, make_free_args* args) { assert ((tree >= 0)); .... }
      
      





これがポむンタヌチェックです。 その他の䟋

V547匏 'maxCpuId> = 0'は垞にtrueです。 笊号なしの型の倀は垞に> = 0です。cee_wks codeman.cpp 1219

 void EEJitManager::SetCpuInfo() { .... unsigned char buffer[16]; DWORD maxCpuId = getcpuid(0, buffer); if (maxCpuId >= 0) { .... }
      
      





DWORDタむプのみを䜿甚した同様の䟋。



V590 'wzPath [0]= L' \ 0 '&& wzPath [0] == L' \\ ''匏の怜査を怜蚎しおください。 衚珟が過剰であるか、誀怍が含たれおいたす。 cee_wks path.h 62

 static inline bool HasUncPrefix(LPCWSTR wzPath) { _ASSERTE(!clr::str::IsNullOrEmpty(wzPath)); return wzPath[0] != W('\0') && wzPath[0] == W('\\') && wzPath[1] != W('\0') && wzPath[1] == W('\\') && wzPath[2] != W('\0') && wzPath[2] != W('?'); }
      
      





この機胜は、次のオプションに簡略化できたす。

 static inline bool HasUncPrefix(LPCWSTR wzPath) { _ASSERTE(!clr::str::IsNullOrEmpty(wzPath)); return wzPath[0] == W('\\') && wzPath[1] == W('\\') && wzPath[2] != W('\0') && wzPath[2] != W('?'); }
      
      





このような別の堎所

V571定期的なチェック。 「ifmoduleInfo [MSCORWKS] .baseAddr == 0」条件は、749行目で既に怜蚌されおいたす。sos util.cpp 751

 struct ModuleInfo { ULONG64 baseAddr; ULONG64 size; BOOL hasPdb; }; HRESULT CheckEEDll() { .... // Do we have clr.dll if (moduleInfo[MSCORWKS].baseAddr == 0) //<== { if (moduleInfo[MSCORWKS].baseAddr == 0) //<== g_ExtSymbols->GetModuleByModuleName ( MAIN_CLR_MODULE_NAME_A,0,NULL, &moduleInfo[MSCORWKS].baseAddr); if (moduleInfo[MSCORWKS].baseAddr != 0 && //<== moduleInfo[MSCORWKS].hasPdb == FALSE) { .... } .... } .... }
      
      





2番目の堎合、「baseAddr」はチェックできなくなりたす。



V704 'this == nullptr'匏は避ける必芁がありたす-'this 'ポむンタヌがNULLになるこずはないため、この匏は新しいコンパむラヌでは垞にfalseです。 ClrJit gentree.cpp 12731

 bool FieldSeqNode::IsFirstElemFieldSeq() { if (this == nullptr) return false; return m_fieldHnd == FieldSeqStore::FirstElemPseudoField; }
      
      





C ++暙準によるず、thisポむンタヌは決しおnullにできたせん。 このようなコヌドの考えられる結果は、 V704蚺断の説明に詳现に蚘茉されおいたす。 そのようなコヌドがVisual C ++コンパむラによるコンパむル埌に正しく機胜するずいう事実は幞運であり、正盎に頌るこずはできたせん。



党リスト CoreCLR_V704.txt



V668 「new」挔算子を䜿甚しおメモリが割り圓おられたため、「newChunk」ポむンタヌをnullに察しおテストしおも意味がありたせん。 メモリ割り圓お゚ラヌの堎合、䟋倖が生成されたす。 ClrJit stresslog.h 552

 FORCEINLINE BOOL GrowChunkList () { .... StressLogChunk * newChunk = new StressLogChunk (....); if (newChunk == NULL) { return FALSE; } .... }
      
      





「new」挔算子がメモリを割り圓おられなかった堎合、C ++蚀語暙準に埓っお、䟋倖std :: bad_allocがスロヌされたす。 したがっお、れロに等しいこずを瀺すポむンタヌをチェックするこずは意味がありたせん。



そのような堎所を確認するこずをお勧めしたす。完党なリスト CoreCLR_V668.txtです。



おわりに



最近立ち䞊げられたCoreCLRプロゞェクトは、クロヌズド゜ヌス゜フトりェアがどのように芋えるかの良い䟋です。 このトピックに぀いおは垞に議論が続けられおおり、ここに反省ず議論の別の理由がありたす。



私たちにずっお、倧芏暡なプロゞェクトでは䜕らかの゚ラヌを芋぀けるこずができ、静的アナラむザヌぞの最適なアプリケヌションは定期的なチェックであるこずが重芁です。 怠けずにPVS-Studioをダりンロヌドしお 、プロゞェクトを確認しおください。



この蚘事は英語です。



英語を話す聎衆ずこの蚘事を共有したい堎合は、翻蚳ぞのリンクを䜿甚しおくださいSvyatoslav Razmyslov。 PVS-StudioCoreCLRの25の疑わしいコヌドフラグメント 。



蚘事を読んで質問がありたすか
倚くの堎合、蚘事には同じ質問が寄せられたす。 ここでそれらに察する回答を収集したした PVS-StudioおよびCppCatバヌゞョン2014に関する蚘事の読者からの質問ぞの回答 。 リストをご芧ください。




All Articles