#contents ---- ここは憑かれたcoderが %%日頃の憂さ晴らしする%% 自戒する為のページです。書き込みの練習場所 [[SandBox]] ではありません。~ *おことわり :この手の事象を押さえたい場合はこんなページ見ないで、こういった本を参照して下さい。|&amazon(4798005274,title); &br;&amazon(475614327X,title); &br;&amazon(4774117870,title); &br;&amazon(4756103642,title); &br;&amazon(4756102107,title); &br;&amazon(489100455X,title); &br;&amazon(4891004568,title); //&br;&amazon(,title); :[[日本電気通信システム(株) > C言語プログラム品質検証ツール Review-C®:http://rec.ncos.co.jp]]| C言語のよくある間違いをツールでチェックし解説する事で、ソフトウェア開発者の育成とプログラム品質向上に貢献致します。 &br;[[C言語のよくある間違いの解説ページ:http://rec.ncos.co.jp/cgi-bin/errorfrmC.cgi?0]] *無知との遭遇 **Case1 : if じゃなく always bool bParam = false; if ( bParam = true ) { fprintf( stdout, "orz\n" ); } else { fprintf( stdout, "(^o^)/\n" ); } 期待出力:(^o^)/~ 出力結果:orz~ 処 方 箋:比較演算子「==」と代入演算子「=」に注意する。~ **Case2 : 突然の別れ static const dParam = 12.0; fprintf( stdout, "%lf\n", dParam ); 期待出力:12.0~ 出力結果:確認不可~ 処 方 箋:ちゃんと型宣言する(この場合はdouble or float)。省略した場合は一般的にはint型となる。~ **Case3 : おまえはもう死んでいる const char* GetChar( const int& nNo ) { char cBuff[ 4 ]; ::_snprintf( cBuff, 4, "%d", nNo ); return cBuff; } int main( int nArgC, char** ppArgV ) { fprintf( stdout, "%s\n", GetChar( 100 ) ); return 0; } 期待出力:100~ 出力結果:解読不可~ 処 方 箋:Auto変数のアドレスは返さない。今回は以下の内のどれかの方法で修正する。~ ++「return cBuff;」=>「return ::strdup( cBuff );」。但し上位側で%%要 delete[]%% 要 ::free() ←間違えてた。 ++「char cBuff[ 4 ];」=>「char* cBuff = new char[ 4 ];」。但し上位側で要 delete[]。 ++「const char* GetChar」=>「std::string GetChar」、「return cBuff;」=>「return std::string( cBuff );」 **Case4 : 僕は死にましぇん static const char* const DAME_DAME = "orz"; class CHoge { public: const char* Get() const { return DAME_DAME; } }; int main( int argc, char* argv[] ) { CHoge* pHoge = NULL; // ぬるぽ! fprintf( stdout, "%s\n", pHoge->Get() ); return 0; } 期待出力:確認不可~ 出力結果:orz~ 処 方 箋:運良く static な変数のみにアクセスしていたので落ちない。そんなコードは書かないようにする。~ **Case4' : 僕は死にましぇん(Part2) class CHoge { public: static const char* Get() { return "orz"; } }; int main( int argc, char* argv[] ) { CHoge* pHoge = NULL; // ぬるぽ! fprintf( stdout, "%s\n", pHoge->Get() ); return 0; } 期待出力:確認不可~ 出力結果:orz~ 処 方 箋:static なメソッドの呼び出しは必ず クラス名::メソッド名 で行う。~ **Case5 : 当然の別れ(※VC++のみ) try { ::AfxThrowUserException(); catch ( CUserException* pException ) { delete pException; fprintf( stdout, "orz\n" ); } 期待出力:orz~ 出力結果:確認不可~ 処 方 箋:CException::Delete()を呼び出すのが正解。CFile::Remove()でやっている事が多い? &br; 或いは try〜catch節 をマクロ版 TRY〜CATCH〜END_CATCH に変更し自動で CException::Delete() を呼び出すようにする。~ **Case6 : 自分の後ろに何かいるのが見えない人 const int nBuffSize = 3; struct TDame { char cDame[ nBuffSize ]; int nDame; } stDame = { 0 }; (void)::strncpy( stDame.cDame, "orz", nBuffSize ); fprintf( stdout, "%s\n", stDame.cDame ); 期待出力:解読不可~ 出力結果:orz~ 処 方 箋:「¥0」の分を含めたバッファを確保する必要がある。sizeof("orz")は「4」となる。 &br; 構造体の8byteアラインメントにより、char[ 3 ]の後ろに1byte分パディングされている。 &br; 上記の例ではゼロクリアしてるので「orz」+「¥0」となりたまたまうまく動作してしまっている。~ **Case7 : おまえはもう死んでいる(Part2) const char* Get( std::auto_ptr<std::string> ptrValue ) { return ptrValue.get()->c_str(); } int main(int argc, char* argv[]) { std::auto_ptr<std::string> ptrValue( new std::string( "orz" ) ); std::auto_ptr< std::string > ptrValue( new std::string( "orz" ) ); fprintf( stdout, "%s\n", Get( ptrValue ) ); fprintf( stdout, "%s\n", Get( ptrValue ) ); return ::_getch(); } 期待出力:orzorz~ 出力結果:「orz」出力後確認不可~ 処 方 箋:「std::auto_ptr<std::string> ptrValue」=>「std::auto_ptr<std::string>& ptrValue」 &br; std::auto_ptrのコピーにより1回目の呼び出し後にインスタンスが破棄されている。~ **Case8 : え・・・そうなん?(※VC++のみ) const char* Get() { __try{ return "(V)゚¥゚(V)"; } __finally{ return "orz"; } } fprintf( stdout, "%s\n", Get() ); 期待出力:(V)゚¥゚(V)~ 出力結果:orz~ 処 方 箋:辛い現実に目を背けず自分の目で確かめて、ありのままを受け入れる。~ **Case9 : ご先祖様も大事だが、今はもっと大事 std::vector<int> vecHoge( 10 ); try { vecHoge.at( -1 ); } catch ( exception& e ) { fprintf( stderr, "orz\n" ); } catch ( std::logic_error& e ) { fprintf( stderr, "Good Job!\n" ); } catch ( std::out_of_range& e ) { fprintf( stderr, "nice catch!\n" ); } 期待出力:nice catch!~ 出力結果:orz~ 処 方 箋:基底クラスは後回しにする。~ **Case10 : ツレない人。 /******************************************************* [名称] IsValid [概要] 正当性チェック [戻り値] bool - チェック結果 (ture:正常終了/false:異常終了) [引数] bChige <IN> - チゲフラグ (ture:チゲ/false:チゲ以外) bNabe <IN> - 鍋フラグ (ture:鍋/false:鍋以外) *******************************************************/ bool IsValid( const bool& bChige, const bool& bNabe ); 処 方 箋:気を抜いていると結構「ture」になっている。~ **Case11 : おまいら typo しる bool IsVarid(); bool SerchValue( const int& ); int GetInfomation(); const char* GetMashineName(); 処 方 箋:[[Spelly:http://www.wndtabs.com/spelly/]] でも使ってみる。~ **Case12 : 初心忘れるべからず std::vector<int> vecHoge; int nResult; // 初期化ド忘れ for ( std::vector<int>::const_iterator itr = vecHoge.begin(); itr != vecHoge.end(); itr++ ) { const int& nHoge = (*itr); if ( nHoge > 10 ) { nResult = 0; break; } else { nResult = 1; } } fprintf( stdout, "%s", ( nResult == 0 ) ? "(V)゚¥゚(V)" : "orz"; 期待出力:(V)゚¥゚(V)~ 出力結果:orz~ 処 方 箋:Auto変数は必ず初期化するという基本に立ち返る。~ **Case13 : 最近連発中・・・ double dA; double dB; double dC; dA = 1.0 * 2.0; dC = 2.0 * 3.0; dB = 3.0 * 4.0; ↓変数の宣言時に値を代入するように修正↓ double dA = 1.0 * 2.0; double dB = 2.0 * 3.0; double dC = 3.0 * 4.0; 処 方 箋:ちゃんと変数の順番を確認し、テストする。orz~ **Case14 : EffectiveC++の教え 一般的な#define定義で配列の要素数を取得する方法。 #define ELEMENT_NUMBER(array) ( sizeof( array ) / sizeof( array[ 0 ] ) ) %%EffectiveC++の教えに従って、#defineを使用しない方法。15分位で出来た。%% template<typename T> static inline int ELEMENT_NUMBER( const T & array ) { return( sizeof( array ) / sizeof( *array ) ); } もっとより良い方法 template< typename T, size_t N > static inline int ELEMENT_NUMBER( T (&) [ N ] ) { return N; } とりあえずVC++6.0〜8.0/g++4.1では上手く動いた。 **Case15 : B級ループ char* pcTemp = NULL; while ( 1 ) { const int nBuffSize = 10; try { pcTemp = new char[ nBuffSize ]; } catch ( std::badalloc& e ) { } catch ( std::bad_alloc& e ) { fprintf( stderr, "%s(%d) : %s\n", __FILE__, __LINE__, e.what() ); ::exit( -1 ); } if ( bFlag1 ) { fprintf( stderr, "%s(%d) : Error@bFlag1\n", __FILE__, __LINE__ ); break; } ・・・・ // break; の書き忘れで永久ループ・・・orz } if ( pcTemp != NULL ) delete [] pcTemp; while文でgoto的なコードを書いて、最後に「break;」書き忘れ。~ デバッグしたら直ぐに分かるバグだが、最近は以下のように記述するようにしている。 char* pcTemp = NULL; do { ・・・・ } while ( 0 ); if ( pcTemp != NULL ) delete [] pcTemp; 処 方 箋:適材適所 do〜while にも有用な場面が有る事を肝に銘じる。 **Case16 : VCおまえだけか! #include <stdlib.h> int main( int argc, char** argv ) { #ifndef DEF_VALUE #define DEF_VALUE #endif DEF_VALUE /*コメントアウトし忘れ?*/ return 0; } |コンパイラ|コンパイル結果|h |VisualC++ 6.0|警告レベル4(/W4)でもエラーなし。| |VisualC++ 7.1|警告レベル4(/W4)でもエラーなし。| |MIPSpro 7.4.2m|cc-1014 cc: WARNING&br;Invalid characters appear before a new-line in the preprocessor directive.| |gcc 3.3.3|warning: extra tokens at end of #endif directive| **Case17 : いつもより余計に回し・・・たらアカンて。 > &ref(STLeraseSample.tar.bz2,JunkBox); **Case18 : 大容量のワイドな文字列で、顔面真っ青 int main(int argc, char* argv[]) { (void)::setlocale( LC_ALL, "japanese" ); fprintf( stdout, "%s, %S\n", "orz", L"orz" ); fwprintf( stdout, L"%S, %s\n", "orz", L"orz" ); return ::fgetc( stdin ); } 処 方 箋:[[MSDN > printf 関数の型フィールド文字:http://msdn.microsoft.com/ja-jp/library/hf4y5e3w.aspx]], [[Manpage of PRINTF:http://www.linux.or.jp/JM/html/LDP_man-pages/man3/printf.3.html]]~ |環境|API|const char*|const wchar_t*|h |VC++|printf/fprintf|''%s''(small s)|''%S''(large s)| |VC++|wprintf/fwprintf|''%S''(large s)|''%s''(small s)| |UNIX系|printf/fprintf|''%s''(small s)|''%ls''| |UNIX系|wprintf/fwprintf|''%s''(small s)|''%ls''| **Case19 : 今更ながら・・・それ間違ってますから〜〜〜残念!(※VC++のみ) wchar_t cBuff[ 10 ]; const wchar_t* const pwcInputValue = L"0123456789ABCDEF"; (void)::_snwprintf( cBuff, sizeof( cBuff ), L"%s", pwcInputValue ); 期待出力:0123456789~ 出力結果:確認不可男~ 処 方 箋:ちゃんと [[リファレンス:http://msdn.microsoft.com/ja-jp/library/2ts7cx93.aspx]] を読む。「n」が付くsnprintf, strncpy等のサイズにはバイト数ではなく、文字数を指定する。 &br; 因みにVC++の_snprintfは [[UNIX系のsnprintf:http://www.linux.or.jp/JM/html/LDP_man-pages/man3/snprintf.3.html]] とは挙動が異なり、サイズで指定した文字数には終端のヌル文字列は含まれない。 &br; 下記のようなコード &ref(snprintf_test.tar.bz2); であればとりあえず問題無し。 const int nBuffSize = 10; TCHAR cBuff[ nBuffSize + 1 ]; // ヌル文字分を別途確保 const TCHAR* const pcInputValue = _T("0123456789ABCDEF"); cBuff[ nBuffSize ] = 0; // ヌル文字分領域にヌル文字をセットしておく (void)::_sntprintf( cBuff, nBuffSize, _T("%s"), pcInputValue ); **Case20 : 環境破壊(※VC++のみ) (void)::SetEnvironmentVariable( "OREORE", "DARESORE" ); (void)::fprintf( stdout, "%s\n", ::getenv( "OREORE" ) ); 期待出力:"DARESORE"~ 出力結果:""(長さゼロの文字列)~ 処 方 箋:SetEnvironmentVariable()で設定された環境変数は、GetEnvironmentVariable()でのみ取得可能となる。 &br; 逆に_putenv()で設定した環境変数は、GetEnvironmentVariable()でも取得可能である。 **Case21 : 痛恨の一撃 「regist」という英語単語は存在しない。「register」とするのが正しい。 元 ネ タ: http://labs.cybozu.co.jp/blog/akky/archives/2005/07/regist.html ~ 処 方 箋:&amazon(4469041580,title); でも手元に置いておくべし。