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.

*1:c,c++に限らず全てのポインタはbad valueで初期化される.bad pointerを参照しようとすると、プログラムの予期しないところでクラッシュして非常にやっかいだ. Java や他の runtime oriented language,ではポインタは常にNULL値で始まるため、そのようなポインタを参照しようとすると必ず検知される。Javaデバッグしやすい理由はここから来る.

ザッカーバーグを演じたアイゼンバーグが本を出したようだ.

映画ソーシャル・ネットワーク で主演マーク・ザッカーバーグを演じたジェシー・アイゼンバーグが本を出したようだ.

多彩系の俳優なのな

www.amazon.co.jp

興味あるひとはnprのインタビューをきいてみるといいだろう

www.npr.org

gdb :データを見るTips

関数の情報をみる : frame

command 機能
frame ,frame [Frame Num] *1 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 ソースコードを逆アセンブルする

bxyxvzz.hatenablog.com

bxyxvzz.hatenablog.com

*1: 番号は0から始まる

*2:関数の呼び出しスタックの各要素はスタックフレームと呼ばれ,関数のアドレスや引数などが格納されている.そして関数呼び出し時に作成され,関数が返るときに削除される.

並列計算でのgdb /openmpi

Ref :

FAQ: Debugging applications in parallel

MPIを使ったプログラムがセグメンテーションフォルトで落ちたとき gdbを使って原因を探求するやり方.

single threadの場合とgdbの基礎は以下を参照

bxyxvzz.hatenablog.com

並列計算でデバッグ@ gdb

mpirun -n 4 xterm -e gdb pararell_program

作戦としてはそれぞれのランクごとにxtermでgdbを起動する.

もちろんこの方法だと,あまりにたくさんの並列数のプログラムをデバッグするのは 大変なので、並列数が少ない場合に起こるバグを調べるのに向いたtipsだ

並列数が大きい場合のgdb tips

もし大並列数のプログラムをデバッグしたい場合は,インタラクティブgdbを使うのはやめたほうが いいだろう.

mpirun gdb –commands=gdb.cmd  mpi_program

gdb.cmdGDBに実行してほしいgdbコマンドが書かれている

$ cat gdb.cmd

run
backtrace
quit

gdb超基本

gdb ? printf/coutデバッグでええわ〜

と言わずに使ってみることにした.

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 逐次実行
print p 式を実行し結果を表示
quit q gdbの終了
return ret 現在の関数をその場で終了
run r プログラム実行
rwatch rw 読み出しウォッチポイント設定
step s ステップイン実行

bxyxvzz.hatenablog.com

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;
}

bxyxvzz.hatenablog.com