ここは憑かれたcoderが 日頃の憂さ晴らしする 自戒する為のページです。書き込みの練習場所 SandBox ではありません。

おことわり

この手の事象を押さえたい場合はこんなページ見ないで、こういった本を参照して下さい。
https://www.amazon.co.jp/dp/4798005274
https://www.amazon.co.jp/dp/475614327X
https://www.amazon.co.jp/dp/4774117870
https://www.amazon.co.jp/dp/4756103642
https://www.amazon.co.jp/dp/4756102107
https://www.amazon.co.jp/dp/489100455X
https://www.amazon.co.jp/dp/4891004568
日本電気通信システム(株) > C言語プログラム品質検証ツール Review-C®
C言語のよくある間違いをツールでチェックし解説する事で、ソフトウェア開発者の育成とプログラム品質向上に貢献致します。
C言語のよくある間違いの解説ページ

無知との遭遇

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変数のアドレスは返さない。今回は以下の内のどれかの方法で修正する。

  1. 「return cBuff;」=>「return ::strdup( cBuff );」。但し上位側で要 delete[] 要 ::free() ←間違えてた。
  2. 「char cBuff[ 4 ];」=>「char* cBuff = new char[ 4 ];」。但し上位側で要 delete[]。
  3. 「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()でやっている事が多い?
      或いは 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」となる。
      構造体の8byteアラインメントにより、char[ 3 ]の後ろに1byte分パディングされている。
      上記の例ではゼロクリアしてるので「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" ) );
   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」
     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 でも使ってみる。

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::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;
}
コンパイラコンパイル結果
VisualC++ 6.0警告レベル4(/W4)でもエラーなし。
VisualC++ 7.1警告レベル4(/W4)でもエラーなし。
MIPSpro 7.4.2mcc-1014 cc: WARNING
Invalid characters appear before a new-line in the preprocessor directive.
gcc 3.3.3warning: extra tokens at end of #endif directive

Case17 : いつもより余計に回し・・・たらアカンて。

fileSTLeraseSample.tar.bz2

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 関数の型フィールド文字, Manpage of PRINTF

環境APIconst char*const wchar_t*
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
出力結果:確認不可男
処 方 箋:ちゃんと リファレンス を読む。「n」が付くsnprintf, strncpy等のサイズにはバイト数ではなく、文字数を指定する。
     因みにVC++の_snprintfは UNIX系のsnprintf とは挙動が異なり、サイズで指定した文字数には終端のヌル文字列は含まれない。
     下記のようなコード filesnprintf_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()でのみ取得可能となる。
     逆に_putenv()で設定した環境変数は、GetEnvironmentVariable()でも取得可能である。

Case21 : 痛恨の一撃

「regist」という英語単語は存在しない。「register」とするのが正しい。

元 ネ タ: http://labs.cybozu.co.jp/blog/akky/archives/2005/07/regist.html
処 方 箋:https://www.amazon.co.jp/dp/4469041580 でも手元に置いておくべし。


Last-modified: 2008-07-02 (水) 12:58:30