C++におけるNULLポインタ
NULLポインタ
NULLはC言語との互換性のために残されているマクロ定義。主にBad Pointer(不適切ポインタ:メモリ確保されていないポインタ)を表すために使われていた*1. C++で(C言語で言う)NULLを表現したい場合、普通は0を使用すべき. 0は任意の型のヌルポインタに変換される事が保証されているので、 どんなNULLマクロよりも安全.
0の欠点
0を使用する方法にもひとつだけ欠点がある. それは、0は任意の型のヌルポインタに変換できますが、 整数型でもあります。 なので、クラス型を引数にもち、ヌルポインタも引数として許す関数と 整数型を引数に取る関数を同時に定義すると、 ヌルを渡した際に常に整数型を引数に取る関数が呼び出されることになる.このような関数のオーバーロードはしてはいけない.
void Func(int i); void Func(bool b); void Func(bool b) { cout <<__PRETTY_FUNCTION__<<endl; } void Func(int b) { cout <<__PRETTY_FUNCTION__<<endl; } void main() { Func(0); Func(NULL); // Func(0)が呼び出される }
[note : g++ -std=c++98 では error: call of overloaded ‘Func(NULL)’ is ambiguous]
Func(NULL)で実際呼び出されるのは Func(int i) である. コンパイラから見たらNULL はポインタではなく、ただの 0 なので当然といえば当然...
NULLの欠点
NULLはマクロ定義なので、処理系依存なので、NULLは処理系によっては 思ったとおりの動作をしない可能性がある。ただしそのよな系は近年は極めてまれである.
c++11
C++11ではnullptrと呼ばれるnull pointerに変換可能だが他の変数に変換不能なキーワード.
Reference
Stroustrup: C++ Style and Technique FAQ
Should I use NULL or 0?
In C++, the definition of NULL is 0, so there is only an aesthetic difference. I prefer to avoid macros, so I use 0. Another problem with NULL is that people sometimes mistakenly believe that it is different from 0 and/or not an integer. In pre-standard code, NULL was/is sometimes defined to something unsuitable and therefore had/has to be avoided. That's less common these days. If you have to name the null pointer, call it nullptr; that's what it's called in C++11. Then, "nullptr" will be a keyword.
ザッカーバーグを演じたアイゼンバーグが本を出したようだ.
gdb :データを見るTips
関数の情報をみる : frame
command | 機能 |
---|---|
frame |
stack frame*2に移動して情報を表示 |
select-frame | 情報を表示しないでstack frameを選択する |
info frame | frame の情報をみる |
bt , bt N, bt -N | backtrace (call stack)の表示 |
bt full | local変数を含めた全ての backtrace (call stack)の表示 |
変数の情報をみる
コードをみる
command | 機能 |
---|---|
list |
ソースコードを表示 |
list | さらにソースコードを表示 |
disas [/m] start,+len | ソースコードを逆アセンブルする |
Linux Mint Tips [未完成]
Linux Mintで画面のロック :Ctrl+Alt+L
並列計算でのgdb /openmpi
Ref :
FAQ: Debugging applications in parallel
MPIを使ったプログラムがセグメンテーションフォルトで落ちたとき gdbを使って原因を探求するやり方.
single threadの場合とgdbの基礎は以下を参照
並列計算でデバッグ@ gdb
mpirun -n 4 xterm -e gdb pararell_program
作戦としてはそれぞれのランクごとにxtermでgdbを起動する.
もちろんこの方法だと,あまりにたくさんの並列数のプログラムをデバッグするのは 大変なので、並列数が少ない場合に起こるバグを調べるのに向いたtipsだ
並列数が大きい場合のgdb tips
もし大並列数のプログラムをデバッグしたい場合は,インタラクティブにgdbを使うのはやめたほうが いいだろう.
mpirun gdb –commands=gdb.cmd mpi_program
gdb.cmd
は GDBに実行してほしいgdbコマンドが書かれている
$ cat gdb.cmd run backtrace quit
gdb超基本
と言わずに使ってみることにした.
gdb
の前にgcc -g
gcc -g yourefile.c
イザ!gdb
gdb a.out
プログラムの実行 !!
run
r
だけ でもええよ!
ブレークポイントの指定
ブレークポイントを設定すると、そこで実行が一時停止させるkとができる
(gdb) break 行番号 (gdb) break 関数名 (gdb) break ファイル名:行番号
b でもいい
gdb の終了方法
(gdb) q
ジャンプ:関数 SKIP!
関数の中の処理まで追い掛けたくない場合。
(gdb) next
n
でもええよ!
関数の中もみたいよ!
関数内の処理を追いかける場合。
(gdb) step
- s もいいよ
ブレークポイントを設定した箇所だが、実行の継続する場合。
(gdb) continue
- c でもええよ。
よく使うコマンドまとめ
元のコマンド | 最短コマンド | 意味 |
---|---|---|
backtrace | bt | 関数呼び出しのトレース |
break | b | ブレークポイントの設定 |
continue | c | プログラムの再開 |
delete | d | ブレークポイントの削除 |
finish | fin | 現在の関数まで実行 |
help | h | ヘルプを表示 |
info | i | 各種情報のリストを表示 |
list | l | ソースプログラムを表示 |
next | n | 逐次実行 |
p | 式を実行し結果を表示 | |
quit | q | gdbの終了 |
return | ret | 現在の関数をその場で終了 |
run | r | プログラム実行 |
rwatch | rw | 読み出しウォッチポイント設定 |
step | s | ステップイン実行 |
c++とboostを用いたシリアライズ
シリアライズとは?
シリアライズとは メモリ上に存在するオブジェクトを、バイト列に変換する処理. このバイト列が、1列に並んでいるため、シリアライズと呼ばれる。 反対の操作はデシリアライズと呼ばれる.
いつ使われる?
シリアライズは、オブジェクトをファイルなどに保存したり、ネットワーク送信したりする際によく利用される。
C++でのシリアライズ 〜クラスをシリアル化できるようにしよう〜
標準ライブラリとして、iostreamといったストリーム入出力ライブラリで、バイト列の入出力は可能だが,オブジェクトをバイト列に変換する機能はない.
Boostライブラリを使うことで、これは解決可能である.
具体的にはboost::serialization
というシリアライズライブラリが提供されている.
これの使い方を以下で説明する.
コード:クラスをシリアライズする
クラスにserialize member関数テンプレートを追加することで、そのクラスがシリアライズ可能になる.
class Customer { private: std::string name_; int age_; friend class boost::serialization::access; template<class Archive> void serialize(Archive& ar, const unsigned int version) { ar & name_; ar & age_; } };
template<class Archive>
void serialize(Archive& ar, const unsigned int version)
というのが,serialize member関数テンプレートである.
以下で詳しく見ていこう
Archive型の引数ar
Archive型の引数ar
には
シリアライズ時にはシリアライズ先となるオブジェクトが、デシリアライズ時にはデシリアライズ元となるオブジェクトが代入される。
今回の例では、main関数で作成したストリームが代入される.
&
演算子
ar & name_; ar & age_;
C++のストリームなどでは、出力に<<演算子、読込に>>演算子を使うが、boost::serializeでは &
を用いて
operatorで、Arichiveをシリアライズ/デシリアライズすることができる。
<<演算子や>>演算子も使えるが、この関数は出力と入力両方を受け持つので&演算子を使う
int version
versionは、保存形式のバージョンを示す数値
friend
指定
boost::serialization
ライブラリのprivateメンバであるserialize関数にアクセスするためにfriend指定が必要
friend指定を省略したいからといって、serialize関数をpublic指定にしてはいけないので注意.
データを入出力
上でつくったクラスのシリアライズ・デシリアライズのやり方を説明する.
基本は標準入出力と同じやり方で可能.即ちファイルオブジェクトの作成と実際のデータの書き出し・読み込みの2つのプロセスを経る.
int main(){ Customer customer; // serialize std::ofstream ofs("output.txt"); boost::archive::text_oarchive oa(ofs); oa << customer; ofs.close(); // deserialize std::ifstream ifs("output.txt"); boost::archive::text_iarchive ia(ifs); ia >> customer; ifs.close(); return 0; }