2007年10月25日

Visual C++の標準ライブラリとcrtdbg.h

先日のcrtdbg.hを用いたメモリリークの自動検出の記事に関連して、開発中のソフトウェアに組み込んだ際に気づいた点がある。それはcrtdbg.hを#includeしなくても、関連の関数が使えてしまうことだ。それでは非常に不気味なので色々試行錯誤をしてみると、どうやらC++の標準ライブラリであるstringの#includeに問題があり、削除してみたところ、関数が使用できなくなった。そこで原因を探るべく、Visual C++のincludeディレクトリのヘッダーファイルを開いてざっと眺めてみたところ、どうやらstringから様々なヘッダーファイルに依存した結果、最終的にcrtdbg.hを#includeしてしまっているようだ。以下に依存関係を列挙しておく。

#include <string> ← #include <istream> ← #include <ostream>
← #include <ios> ← #include <xlocnum> ← #include <streambuf>
← #include <xiosbase> ← #include <xlocale> ← #include <xdebug>

最後のヘッダーファイルであるxdebugの22行目より

 #if defined(_DEBUG)
  //省略

  #include <crtdbg.h>

マクロ名_DEBUG、つまりVisual StudioのIDEでReleaseではなくDebugでビルドした場合に、問答無用でcrtdbg.hがロードされるわけである。何が問題なのかというと、crtdbg.hではグローバルスコープのnew演算子をオーバーロードしているので、仮に別途new演算子をオーバーロードしている場合にエラーとなってしまうのである。さらに他にもistreamをロードしている標準ライブラリとして、iostreamやiomanip、ios、sstreamなど多数のライブラリがロードを行っているようである。Visual C++で標準ライブラリを用いてなお且つnew演算子をオーバーロードする場合の解決策として、直感的にはxdebugの問題箇所にメスを入れれば良いが、根本的な問題はVisual C++の標準ライブラリの実装の問題なのでスマートではない。


2007年10月24日

メモリリークの自動検出と特定の方法

C/C++では他の言語ではあまりお目にかからない、メモリアロケーションを扱うことが出来る。便利な反面、使い方のお作法を間違えると利点以上の代償を払うことになる。メモリリークが発生する大きな要因としては、確保したメモリの開放のし忘れである。Javaなどとは違いガーベージコレクタは標準ではサポートされておらず、明示的な開放の記述が必要である。
Visual C++では、crtdbg.hというヘッダーにメモリリークを検出し、デバッグログに出力を行ってくれる独自の関数が定義されている。以下に使い方や応用例を示しておく。


まず、ヘッダーをロードしている記述のある場所を以下のように書き換えを行う。

#define _CRTDBG_MAP_ALLOC

//ここに任意の#includeを記述する
//例えばwindows.hやstdio.hなど

#ifdef _DEBUG
  #include <crtdbg.h>
  #define new  ::new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

このような順番で記述を行う理由としては、まず下の方の#defineで既存のnewをオリジナルの記述へと置き換えを行っているが、これはcrtdbg.hのバグ対策用であり、new演算子を用いた場合に適切なファイル名がデバッグログに出力されない問題を修正するためのものであるが、たとえばこの記述を標準ライブラリより前に行ってしまうと、プリプロセス時に関係の無いnew演算子まで置き換えを行ってしまい、エラーを起こしてしまうからである。なお#defineで_CRTDBG_MAP_ALLOCの定義を一番先頭で行っているのには深い理由があるのだが、その解説は後日行う。

次にmain関数もしくはWinMain関数の先頭で以下の関数を呼び出す。

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
これは、プログラム終了と同時にVisual Studioのデバッグログにメモリリークの有無を出力することを指定する記述である。

以上の記述により、malloc(int);やnew int;などを行ったままでプログラムを終了すると、デバッグログにメモリリークの詳細が出力される。

ちなみに、もしGUIやDirectXを用いたソフトウェアを開発している場合に、AllocConsoleなどで確保したコンソールにメモリリークのログを出力したい場合は、WinMain関数の一番最後の行に以下の記述を行うことにより可能となる。

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);  //標準出力のハンドルを取得
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);  //ログの出力対象をファイルハンドルに指定
_CrtSetReportFile(_CRT_WARN,hStdout);  //ワーニングの出力対象を第二引数のストリームに指定
_CrtDumpMemoryLeaks();  //実行時点での開放されていないメモリの一覧を表示


参考:
メモリ リークの検出と特定
_CRTDBG_MAP_ALLOC マクロは、正常に機能しません。

2007年10月11日

東芝製扇風機F-LH25Xについて

20071011_1.jpg
メーカーHPへ

今年の夏の初めに購入した東芝製扇風機のF-LH25X。
同一価格帯の中ではデザインも良く、タイマーや風量調整、リズム風など一通りの機能が揃っているのが購入の決め手。

しかし購入当時から首振り機能をオフにするスイッチを引っ張ると、一定の周期で扇風機の首元から「カタカタ」、「カチッカチッ」と異音が発生する。その時は「そういう仕様」なんだと思い込んでいたが、就寝時にその音が結構気になり始めたので製品の型番で検索をしてみると、同一の不良の事例がありどうも初期不良の多い機種でもあるらしい。基本的に扇風機は購入してから数年と長く使うものなので、保障期間が有効なうちに、購入した家電量販店へ持ち込み修理をお願いしてもらうことに。ちなみに異音の原因として推測だが、首振り機能のスイッチ部分が結構緩く、それが原因で他の部品に接触している感じがした。もしくは部品の取り付け不良といったところだろう。

そして先日、家電量販店から電話があり、結局製品は新品と交換とのこと。単純に分解して該当部品の交換だけで修理できないということなのだろうか。交換品は問題であった首振り機能のスイッチ部分の緩さが解消されており、しっかり動作するようになっていた。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。