カテゴリー「C/C++」の10件の記事

【C/C++】 2次元配列の引数渡し

今日、ふと悩んだ2次元配列を関数引数で渡す方法。
やりたいのはつまり...

static void sub_func( ☆ )
{
    /* ここで2次元配列A[5][3]の各要素をA[x][y]のように読み書きする */
}
void func( void )
{
    int A[5][3];
    sub_func( A );
}

という事。
問題となるのは「☆」の部分をどのようにすれば良いのか?
いくつか方法を思いつきますね。

1. static void sub_func( int* a )

これはあくまで2次元配列の先頭アドレスをポインタ変数として受け取る形。この形式で渡すと2次元配列を格納したメモリにアクセスする事はできるけど、2次元配列である情報が渡らない。つまりA[x][y]のような形式でアクセスできない。
よく見かけるコートは、この方法でアドレスを渡して、その後中の要素番号を計算しつつアクセスする方法。でもそれじゃコンパイラの恩恵は得られないし、第一エレガントじゃない。

2. static void sub_func( int** a )

これは間違い。コンパイルできない。Aはあくまで[5][3]要素を持った2次元配列の名前であってポインタポインタで指定できるかどうか知ったこっちゃない。というかポインタポインタではない。

3. static void sub_func( int a[] )

当然これも間違い。引数は1次元配列を期待している。

4. static void sub_func( int* a[] )

一見よさげに見えるけど(実は最初こう記述してしまった)これも誤り。これだとint型のポインタ変数の配列が渡される事を期待する。2次元配列はポインタ配列では管理されていない。

5. static void sub_func( int a[5][3] )

これが正解。要素数が固定であれば、直感的にも分かりやすい。注意点としてC言語ではスカラ型データであれば値渡し(値がコピーされる)になるけど、配列の場合は無条件で先頭アドレスの参照渡しになる事。ちなみに最初の次元数は省略できる。つまり

static void sub_func( int a[][3] )

という記述も許される。2次元配列の場合2次元目の数が分かれば1次元目のインクリメントが何バイト単位かが計算できるから。

6. static void sub_func( int (*a)[] )

実はこれも正解。4との違いは括弧の位置。これが正解である事を認識するのにちょっと手間取った。

6番目が正解な理由は1次元配列を考えてみると簡単。

static void sub_func( int a[] )
static void sub_func( int *a )

この2つは呼び出し側としては等価になる。C言語の規約では配列を指定した場合には無条件で参照渡しになるから。ちなみに正確に言うと受け側での意味は異なる。前者は配列を受け取ったが、後者は配列が格納されている先頭アドレスをポインタ変数で受け取っている。これは「a = 数値」といった代入文が前者では記述できず、後者で記述できる事から分かる。後者の場合、スタック上に確保されたaというポインタ変数に呼び出し側配列が記録されているメモリの先頭アドレスが記録されるから。通常は受け側で同じ使い方をするなら同じコードが出力されるけど、コンパイラによっては前者の方が効率が良いかもね。

ちょっと脱線した。
さて、上記の2行を見れば分かるとおり、C言語の呼び出し側にとって「a[]」と「*a」は等価になる。つまり先の2次元配列「int a[][3]」は「int (*a)[3]」と等価であると言える。

では上記3がなぜダメなのか。もうお分かりでしょう。「int *a[3]」と「int (*a)[3]」は異なる。前者は「int型のポインタ変数が3つからなる配列」であるのに対して後者は「int型3つからなる配列の配列の先頭アドレスを格納したポインタ」(配列が2重になっているのはタイプミスではないですよ)という事。

やっぱりC言語のポインタは奥が深い...

【C++】 VisualStudio 2010 でのsnprintf関数

snprintfを使っていてgccでは普通にビルド出来てたプログラムをVisualStudio 2010でビルドしたら snprintf 関数が定義されていないってエラーになりました。
「あ〜、ヘッダファイルのinclude忘れか」
と思いきや、vectorコンテナを使ってるからstdio.hもincludeされてるなぁ。
うん、明示的にincludeしても変わらん。

標準関数なのになぜ〜と思って stdio.h を覗いてみると...
なんかsnprintfが無いっぽい。

そうなんですなぁ。理由はなぜだか知らんけど、VisualStudioについている標準ライブラリではsnprintfはサポートしていないみたいです。(セキュリティの関係でいくつかの関数が無くなったとは聞いた事があるのやけど、snprintfやからなぁ...理由がわからん)
似たような名前の関数が色々あるのやけど、とりあえずこういう時は処理系依存の関数の定番

_snprintf

を使う事にして解決。
実際には共通ヘッダファイルで

#define snprintf    _snprintf

としましたダ。
ふぅ...

【C/C++】 streamクラスのeofメンバ

以下の記事でstreamクラスのeofの扱いが難しいという記事を書いたんやけど、まぁこれやったらと許容できる方法を考えたので載せておこう。

【C/C++】ifstreamのeofメンバ関数(違和感のある挙動)

ちなみに以前はifstreamについて書いたんやけど、調べてみるとstream系のeofは全て同じ実装みたい。
つまり、実際に最終要素にアクセスした後でないと、正確なeofを検出できないと。

そこで、以下のようにすると、うまく動きます。
ん〜、読み出し部分が2行あって気持ち悪いけど、一応これやったらちゃんと対応できてると思うんよね。

istringstream s(buff);
string line;

s >> line;                <-- ミソ
while( !s.eof() ) {
    〜何らかの処理〜
    s >> line;
}

単純にwhileループをeofメンバで終了判定するのやけど、その時にwhile文の直前に一回読み出しを行ってしまおうと。

要素数が0の場合:  while文に入らず終了(処理されない)
要素数が1の場合:  while文に入り処理が1回実行。次のループ条件が偽になりループから抜ける。
要素数が2の場合:  上記の繰り返し

という事で、うまく行きそうですな。
ん〜、なるほど。こういう思想で設計されているんやね。

【C/C++】 マクロと関数呼び出し

仕事でちょっとはまったC言語の問題。
マルチプラットフォームでビルドしてたんやけど、謎なエラーが表示されててん。

コンパイルしたコードは以下の通り。

if ( condition ) test_func( ... );
else              test_func( ... );

エラー表示されたメッセージは

error: expected primary-expression before ‘else’

ってな感じでしたわ。
なんで else がパースされへんのか?と思って色々調査。

if ( condition ) test_func( ... )
else              test_func( ... );

とすると、コンパイルできました。
これはおかしい。
結局、よく調べてみると test_func というのは関数やなくて、マクロ関数で定義されていました。

#define test_func( ... ) { hogehoge(); fugafuga; ... }

こんな感じ。そのためプリプロで展開すると

if ( condition ) { hogehoge(); fugafuga(); ... };
else              { hogehoge(); fugafuga(); ... };

といった具合になって、1行目の末尾のセミコロンが悪さをしていました。
今回の問題は実はもうちょっと複雑で、片方のプラットフォームでは test_func は外部関数、もう片方のプラットフォームではマクロ関数で定義されていました。そのため、同じソースコードなのにプラットフォーム毎にエラーが起きたり起きなかったりと不可解な挙動をしたわけです。で、問題は回避方法。片方がマクロ関数である限り、セミコロンをつける事はできひんけど、外部関数の呼び出しでエラーになっていまう。逆もしかり。一番手軽な修正方法は中括弧をつける事かね。

if ( condition ) { test_func( ... ); }
else              { test_func( ... ); }

こんな感じにすればマクロ関数でも外部関数でも動作する。
せやけど、シングルステートメントの if〜else 文が書かれへんよなぁ。

色々悩んでいたんやけど、ふと昔見たことがあるテクニックを思い出しました。

#define test_func( ... ) do { hogehoge(); fugafuga(); } while(0)

って定義すれば良いんやった。
最後にセミコロンはつけへんように注意。
このマクロ関数はステートメントの末尾にセミコロンを持つ普通の関数のように扱えるわけや。
while(0)は結局ループなしになるので、コンパイラの最適化でループ自体が削除されて無駄な命令が出力される事もない。(はず。コンパイラの最適化機能によるかね。)

という訳で、無事に解決しました。

【C/C++】 演算子のオーバーロード

最近C++でやたら演算子のオーバーロードをする事が多くて色々調べたり実験したりしていたんやけど、ここいらで覚書きをしておこう。

演算子をオーバーロードする目的には色々あるけど、主にオブジェクトに対して以下のような記述方法を使いたい時が上げられるかな?

obj A;
obj B;
obj C;
A = B;
A += B;
A = B + C;

もちろん + 以外にも -(sub) *(mul) /(div) &(and) |(or) ^(xor) <<(lshift) >>(rshift) などなど色々あるわけでさ。
それからコピーコンストラクタにポインタ参照、キャスト演算子などキリがないね。
で、↑のようなオブジェクト同士の演算であれば結構簡単。

class obj {
public:
    void operator = ( obj o );
    obj& operator + ( const obj& o );
    obj& operaotr += ( const obj& o );
};

こんな感じ。
明示的に整数型を引数にとりたければ(つまり A += 1; などと記述したければ)

obj& operator +=( int i );

で対応できる。もしくは

class obj {
    obj( int i );
    obj& operator += ( const obj& o );
};

でも可能。その代わり整数の1を記録するためのテンポラリオブジェクトが生成されてしまうけどね。

さてさて、この辺の関数、全て obj& を戻り値としていたけど、どうやって値を返すのか?
これも結構簡単。

class obj {
    obj& operator += ( const obj& o ) {
        // なんらかの処理
        return *this;  // 自分自身を返す
    }
};

とまぁ、*thisを返却すれば自分自身が返ります。
もちろん他のオブジェクトを返しても全く問題ないのやけど、ローカルオブジェクトだけは返却しないようにね。
一応一通りの演算子オーバーロード定義を書いておこう。
ちなみに対象は自身のオブジェクト型として、戻り型も自分自身とした場合です。
戻り型は必要に応じて変更してくださいな。

operator int( void ) { return i; }    // キャストオーバーロード   int a = (int)obj; 他のキャスト型も可能
obj& operator = ( const obj& b );   // 代入  a = b;
void operator ()( void );               // ファンクタ(関数オブジェクト)
obj& operator - ( void );              // 符号反転  -a
// 比較
bool operator ==( const obj& b );   // 比較  if ( a == b ) ...
bool operator !=( const obj& b );   // 比較  if ( a != b ) ...
bool operator <( const obj& b );   // 比較  if ( a < b ) ...
bool operator <=( const obj& b );   // 比較  if ( a <= b ) ...
bool operator >( const obj& b );   // 比較  if ( a > b ) ...
bool operator >=( const obj& b );   // 比較  if ( a >= b ) ...
// 単項演算
obj& operator += ( const obj& b );  // 加算  a += b;
obj& operator -= ( const obj& b );  // 減算  a -= b;
obj& operator *= ( const obj& b );  // 乗算  a *= b;
obj& operator /= ( const obj& b );  // 除算  a /= b;
obj& operator <<=( const obj& b );  // シフト  a <<= b;
obj& operator >>=( const obj& b );  // シフト  a >>= b;
obj& operator ++( void );               // 加算  ++a;
obj& operator ++( int );                 // 加算  a++;
obj& operator --( void );               // 加算  --a;
obj& operator --( int );                 // 加算  a--;
obj& operator &= ( const obj& b );  // AND  a &= b;
obj& operator |= ( const obj& b );  // OR  a |= b;
obj& operator ^= ( const obj& b );  // XOR  a ^= b;
// 2項演算
obj& operator + ( const obj& b );  // 加算  a + b;
obj& operator - ( const obj& b );  // 減算  a - b;
obj& operator * ( const obj& b );  // 乗算  a * b;
obj& operator / ( const obj& b );  // 除算  a / b;
obj& operator <<( const obj& b );  // シフト  a << b;
obj& operator >>( const obj& b );  // シフト  a >> b;
obj& operator & ( const obj& b );  // AND  a & b;
obj& operator | ( const obj& b );  // OR  a | b;
obj& operator ^ ( const obj& b );  // XOR  a ^ b;

さてと、ここからが本題。
上の定義を使うとオブジェクトに対するオペレータのオーバーロードが可能になる。
つまり

A = B + 1

とかがオブジェクトに対して行えるわけ。
この場合の処理順序は

  1. オブジェクトBに対して operator + (int i) 呼び出し
  2. オブジェクトAに対して operator =( const obj& b ) 呼び出し

になる。
ここで問題が起きる。もし上式を

A = 1 + B

と記述した場合、どうなるだろう?
この場合、1に対して + 演算子が処理される。でも 1 は組み込み型の int と解釈され、オブジェクトの演算子オーバーロードは影響しない。こういう時は項を入れ替えれば上の式と同じになるので、オブジェクトのオーバーロード関数で処理できるのやけど...。

で、こんな時のためにあるのがグローバル演算子のオーバーロード。
今までのは全てクラスに定義しているため処理対象はオブジェクトという事になっていたけど、グローバル演算子をオーバーロードしてしまえば組み込み型だろうが関係ないわけですな。
よって、

A = 1 + B

を処理するためには

obj& operator + ( int i, obj& b );

というグローバル演算子のオーバーロード関数を記述すれば良い。
こちらの関数はクラスの演算子オーバーロード関数と異なり、2項をとる。
上の式だと「整数 + オブジェクト」なので、第1引数が整数、第2引数がオブジェクトとなる関数を定義すればよい。
ちなみに

int operator + ( int a, int b );

のような組み込み型のみに対してのオーバーロードは禁止されているらしい。
(そりゃそうだ...

【C/C++】 bitset (STL)

STLのコンテナではないけど、C++標準に含まれてるテンプレートクラスでbitsetというのがあります。これは中々面白い。要はビット単位でデータを保持するクラスなんやけど、何につかうか?と言うと、例えば

  • コマンドオプションのフラグ情報保持
  • メモリマネージャのセグメント管理
  • データキャッシュのフラグ管理

ちょっと変わった所では

  • 可変長ビット演算

なんて物も考えられるかな?
まずは簡単な使い方。
128ビットの領域を確保して任意のビットの値を取得したりセットしたり...

bitset<128> bits;
bits.reset();                       // 全ビットクリア
bits[10] = 1;                      // 10bit目を1に設定
cout << bits[10] << endl;    // 10bit目の値を表示
bits.set( 10, 0 );                 // 10bit目を0に設定

[]演算子がオーバロードされているから、特定ビットの値を取得したり書き換えたりが簡単にできる。
自分で書いてみると分かるけど、これと同じ挙動するクラスを書くのはちょっと面倒。
コード量とかって言うより適切なバイト位置を経産してビットシフトして...って実装をすると結構バグるもんです。

ただ、このままやと単なるビットしか扱えないのでちょっと不便。
という事で、以下のようなメンバ関数があったら良いんではないかな?

void set_value( int p, int n, int len )
{
    for ( int i = 0; i < len; i++ )
        bits[p+i] = (n >> i) & 1;
}

このコードでは、ビット位置の大きい方がMSBとして処理しています。
bitsetのoperator[]演算子では0以外の値は全て1とみなすようなので、最下位ビットだけを論理積で取得しておく必要があるようやね。同じような感じでget_valueも作っておくと良いでしょう。

bitsetのメンバ一覧を書いておきます。(気づいたものだけやから全部ではないかも)

bitset<N>( 初期値 )            // コンストラクタで初期値(32ビットまで可)が指定可能
bitset<N>::reset()               // 全ビットクリア
bitset<N>::set(p)                // pビット目を1にする
bitset<N>::set(p,n)              // pビット目をnにする ( n == 0 ) ? 0 : 1
bitset<N>::operator[](p, n)    // pビット目をnにする
bitset<N>::operator<<(s)      // 全体をsビット左シフト(ビット番号の大きい方にシフト)
bitset<N>::operator>>(s)      // 全体をsビット右シフト(ビット番号の小さい方にシフト)

【C/C++】ifstreamのeofメンバ関数(違和感がある挙動)

ifstreamでバイナリファイルを読み込んだ時の挙動で気になる事が...
通常、バイナリファイルを全て読み込む時には

ifstream ifs
ifs.open( ファイル名, ios::in | ios::binary );
if ( ifs ) {
    while( !ifs.eof() ) {
        ifs.read( (char*)変数, sizeof(変数) );
    }
}

ってな感じで変数に逐次読み込んで行くのだけど、これだと何故かバイナリファイル中のサイズ+1のreadがされてしまうみたい。なぜかeof()がtrueになるのは、ファイルの全データを読み込んだ後のread関数を呼んだ後のような...。
取り合えず以下のようにすると、その問題を回避できるみたい。

ifstream ifs
ifs.open( ファイル名, ios::in | ios::binary );
if ( ifs ) {
    while( true ) {
        ifs.read( (char*)変数, sizeof(変数) );
        if ( ifs.eof() ) break;
    }
}

readを呼んだ後にeofをチェックしてあげると。
でも、この場合空ファイル(サイズ0バイトのファイル)を開くと必ず1ワードの無効値が取得できてしまう。それを避けるうまい方法がイマイチ思いつかない...

なんでこうなるか?という原因やけど、おそらくifstreamクラスの中でのeofチェックは実際にデータ取得をする時に判定されているのではないかと推測される。つまり、ifs.eof()はファイルの終端まで読み終わったかどうかを知る関数であって、ストリームポインタがファイル終端にある事を知る関数ではないのではないかと。その証拠に、最初のコードで0バイトのファイルを開くとwhileの条件文が真となってループの中が1度だけ実行される。

こうなると、やっぱりストリームイテレータを使ってファイルから読み込むのが良いのやろうけど、そっちはそっちで文字列読み込み -> 数値変換って挙動をするようで...。>>演算子をオーバーロードするという手もあるのだけど、他にも影響でそうやしなぁ。(ビットシフトする箇所あるし...あ、でもfstreamに対してビットシフトはしないから問題ないのか?)

今日は、この挙動に違和感を感じた一日でしたなぁ。

(2009/12/12追記)

一応の解決をみましたので、解決法にリンク貼っておきます。

【C/C++】 streamクラスのeofメンバ

【C/C++】 iostreamのマニピュレータ

iostreamのマニピュレータ一覧を載せておこうっと。
使い方は cout << マニピュレータ とか cin >> マニピュレータでOK。
マニピュレータを使う場合は #include <iomanip> しておく。

8進数                oct
10進数              dec
16進数              hex
改行                 endl  (ostreamのみ)
NULL文字          ends  (ostreamのみ)
フラッシュ           flush  (ostreamのみ)
空白削除           ws    (istreamのみ)
基数設定           setbase(n)   n=0,8,10,16
幅指定              setw(n)
フラグのリセット  resetiosflags(i)
フラグのセット    setiosflags(i)
文字埋め          setfill(c)
精度設定          setprecision(n)    n:桁

【C/C++】 標準出力への整形出力

C++の標準出力ではprintfも使えるけど、coutも使えるね。
いっつも悩むのが、どっちを使うか?って事で...
これには多分答えが無いような気がする。
printfだと行単位で任意のフォーマットで表示するのが楽。
coutだと複数行の記述でも(ある程度)見やすく書ける。
って所かなぁ。でもprintfでの複数行の表示はprintf自体を分ければ良いわけやし一長一短かな?

printfは良く使うけどcoutは複雑なんで、整形表示の書式を覚書きしときますか。
coutの書式はfstreamのオブジェクトにも使えるはず。つまりcoutなら標準出力やけど、出力ファイルオブジェクト(ofstrem)であればファイルに出力できると思われ。

何はともあれincludeするヘッダファイルを。

#include <iostream>
#include <iomanip>

変数の内容を出力

cout << 変数

指定の幅で出力

cout << setw(文字幅) << 変数;

指定した幅で頭に0詰めして出力

cout << setw(文字幅) << setfill('0') << 変数;

16進数で表示

cout << hex << 変数;

10進数で表示

cout << dec << 変数;

左詰めで出力

cout << left << 変数;

右詰めで出力

cout << right << 変数;

進数設定と詰め方の設定は内部変数に記録されるようです。
よって、一度でも

cout << hex;

とすると、その後の数値型は16進数で表示されてしまう。10進数に戻すには明示的に

cout << dec;

とする必要がありますね。
右詰め、左詰めも同じ。
ちょっと注意が必要やね。

まぁ、この辺を含めて考えると以外とprintfの方が書きやすかったりする。
coutのメリットは出力する変数の数が多くなっても記述しやすいって事かなぁ。
printfの場合フォーマットに対する引数が可変数引数で渡されてるからフォーマットから要求される変数の数と引数で指定した変数の数が合わないと不正メモリへのアクセスも発生しやすいし、変数の数を変更するのも手間やったりするし。

とは言え、結論は好みって事かなぁ。

【C/C++】 basic_stringクラス

ちょっと仕事でとある組み込み用プロセッサのアセンブラを作ってみようと思っとります。アセンブラに必要な機能として

プリプロセス(トリミング、マクロ展開)
パーシング
ラベル処理
ニモーニック→マシン語への変換

ってな位は必要になるわけです。ここで難しいのがパーシング(構文解析)で、C言語のような高級言語のコンパイラだったらyaccとかbisonとかパーサ出力してくれるコンパイラコンパイラで処理するのだけど、アセンブラ位でそこまでするのも何だかなぁ。しかも今回対象としているアセンブラはマイクロコード用のアセンブラで文法が特殊な構造してるから、ちょいっと独自に作ってみるかと思うわけです。

で、そうなるとどうやって作るかという事ですが、アセンブラは文字列処理を行うわけやし、ラベルの処理とかもあってデータ数が可変になる。あとPCで動作させるという事であればメモリや処理速度には余裕がありそうやね。文字列処理には向いていないけど、C++を使う事にするとSTLを使わない理由はない。Boost C++も使いたいけど標準でない事と環境のセットアップが面倒なんで、C++/STLで実装する事にしました。

そこで使う事になるテンプレートクラスは

string (厳密にはSTLではないけど)
vector (テキストファイルのバッファリング)
map (ラベル処理)

ってな感じかな。マシン語への変換はデザインパターンから Chain of Responsibility パターンを使うと楽そうですな。

話が大分脱線したけど、今回は復習ついでにstringクラスのメンバと使い方を調べてみようかと。
前にSTL使ったのって、もうかれこれ数年前です〜っかり忘れてるんだなぁ。この所組み込みばっかりやったし...

basic_stringクラスの主要メンバ関数

append   文字列の追加
assign     文字列の代入
insert      文字列の挿入
replace   文字列の置換
substr     文字列の一部抽出
at           特定文字への参照ポインタ
begin      文字列の先頭iterator取得
end        文字列の末尾+1 iterator取得
compare 文字列の比較
copy      文字列のコピー
empty    文字列の空チェック
erase     文字列の削除
find        指定文字列の検索
length     文字列の長さ

とりあえずstringクラスだけ。