カテゴリー「C」の51件の記事

2008年7月25日 (金)

[プログラミング] Inside a profiler

最近はネタ記事ばかり投稿してるかときちです。どうもこんにちは。

「いや待て、このままでは只のバカと思われてしまうよ!」

という危機感を持ちましたので、この前社内で発表したプロファイラの仕組みについてのスライドを公開することにします。
「プロファイラとは何か」から始まり、実際にプロファイラがどのような仕組みで動くか、動いているかを紹介しています・・・が、そんなことよりも勉強しているボクを見てください。

情報自体はインターネットからの寄せ集めですが、まあ初心者向けとしてそこそこ面白がってくれました。つっこみどころとかあると思いますので、コメント待ってます。

それにしてもスライド作成中に、Windowsの商業向けでは唯一のVisual StudioがTeam Edition(20万円以上もする!)でなければプロファイラが付属していないことを知りました。

Macではより高機能なDTraceベースのInstrumentsなんかが無償で付いていると言うのに、なんだそれ。開発者をなめてんのか、と。

| | コメント (2) | トラックバック (0)

2008年3月22日 (土)

[C] memcpy()とmemmove()の違いを調べた

Code Reading、ということでCのメモリコピー関数memcpy()とmemmove()の違いについて調べてみることにしました。APIは以下のとおりです。

void *memcpy(void *restrict s1, const void *restrict s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);
※restrict修飾子についてはこちらを参照

で、この2つをなぜ調べる気になったのかというと、memmove(3)のmanページに

The memmove() function copies n bytes from string s2 to string s1.  The two strings may overlap; the copy is always done in a non-destructive manner.

という記述があったから。memcpy()はs1とs2の指す領域がオーバーラップした時の振る舞いは未定義だとありますが、memmove()は上記のとおり正しくコピーされることを保証しています。
ソース上ではどのように違うのか、と気になったのです。

続きを読む "[C] memcpy()とmemmove()の違いを調べた"

| | コメント (0) | トラックバック (0)

2008年3月14日 (金)

[C] 関数内の関数の評価順序は未定義

タイトルそのままなのですが、

void foo(int a, int b, int c) { return; }

int main() {
    foo(printf("A"), printf("B"), printf("C"));
    return 0;
}

Forest というソースを実行した時に、ABCと表示されるのかCBAと表示されるのか、はたまた別の順序になるかは、仕様として決められていません。関数引数が評価される順序は、つまり不定なのです。

しかも、GCCだとバージョンによっても評価順序は変わるとのこと。

今回はprintf()というお互いに副作用の無い(*)関数を評価させましたが、互いの実行順序が意味を持つような場合、引数に関数を直接渡してしまうのではなく、事前に順番通り呼んでから引数に渡すように注意してください。

int main() {
    int a, b, c;
    a = printf("A"); b = printf("B"); c = printf("C");
    foo(a, b, c);
    return 0;
}

以上です。・・・なんて偉そうな事を書いていますが、実は順序不定である事を今日知ったことは内緒です。

【注意】
上ではprintf()を副作用がないと書きましたが、厳密には標準出力に対して副作用があるので注意してください。この場合だと、「ABC」または「CBA」と表示される事を期待したプログラムを書いてはいけない、ということです。

| | コメント (0) | トラックバック (0)

2008年1月13日 (日)

[Python][C] 立方根の値を求めるプログラム

今日はこれまでの無理がたたり、一日中寝込んでしまっていました。
お昼ご飯を食べてこたつに入っていたら、そのまま熟睡。気づいたら真っ暗。
3連休の貴重な1日をほぼ無駄にしてしまった・・・。皆さんはどうお過ごしですか?

えーと、さて。夜は気を取り直して勉強です。

今日は数学の地力を付けよう、という目標を立てていて、虚数の概要を調べていました。
すると「カルダノの公式」という3次方程式の解法が出てきて、その中で立方根が出てきました。で、チラシの裏で数式をいじっていたら「1331^(1/3)」を解くはめになったのですが、これが何なのかよく分からない。

ひょっとしたら小数かもしれないな、なんて思った(思ってしまった。後述)のでコンピュータに計算させようとしたのですが、立方根を計算してくれるライブラリがすぐにありませんでした。

10^3=1000で、さらに12^3=1728、それに下一桁が1ということで、ちょっと考えればすぐに分かったのでしょうが・・・何で気づかなかったんだろう?

探してインストール、というのも面倒だったので、自分で作ってみることにしました。
※誰か適切なライブラリを知っていたら教えてください。

続きを読む "[Python][C] 立方根の値を求めるプログラム"

| | コメント (2) | トラックバック (0)

2007年11月 7日 (水)

SNMPの最新実装を追う・・・つもりが

注:技術的な話です。つまんないですからね。

仕事でIPv6まわりのSNMP実装状況を見ていました。以下はそのときのメモです。

続きを読む "SNMPの最新実装を追う・・・つもりが"

| | コメント (4) | トラックバック (0)

2007年10月10日 (水)

ダミー関数呼び出しの無視できないオーバーヘッド

おはようございます。夢でわがままな男子(小学4年生)が出てきて、その子に何が良いことか悪いことかを必死になって教え続けたせいでグッタリした寝起きのカトキチです。いったい何なんだ・・・

さて、C/C++プログラムの手軽なデバッグ方法として、printf()を埋め込んでその時々の変数や状態を表示させる方法があります。

っていうか、組み込み向けで、エミュレータもなく、リモートデバッガも遅くて使えないような環境ではprintf()をコンソールに出力させる方法くらいしかないのですが・・・。

他の言語でも一般的な方法ですね。C/C++ではさらに、埋め込んだprintf()をそのまま残してリリースできるように、printf()にラッパー関数をかぶせておいて、リリース時にはその関数ごと無効化してしまう、という方法もよく行います。assert()のようなイメージですね。例えば、

(1)
#if _DEBUG
#define dbg(fmt, ...) printf("debug:" fmt, __VA_ARGS__) // __VA_ARGS__はC99の新機能
#else
#define dbg(...)
#endif

(2)
#if _DEBUG
#define dbg real_dbg
#else
#define dbg dummy_dbg
#endif

(1),(2)どちらもデバッグプリントの実現方法なんですが、(1)ではマクロでデバッグプリントを実現する方法で、無効化時にはプリプロセッサで文ごと削除する方法、(2)ではマクロの定義状況によってデバッグ用の正式な関数(real_dbg)をコールするか、ただ0を返すダミー関数(dummy_dbg)をコールするかを分ける方法です。
これによってリリース時の動作とデバッグ時の動作を切り替えられるようになります。

ところが、このうち(2)の方法には落とし穴があります。
関数呼び出しをダミー関数で削除したとしても、ダミー関数をコールすることによるオーバーヘッドが残ってしまうことです。

続きを読む "ダミー関数呼び出しの無視できないオーバーヘッド"

| | コメント (2) | トラックバック (0)

2007年10月 6日 (土)

文字列操作関数を自分なりに改善してみる

Cライブラリは文字列操作に対して全くと言っていいほどAPIを提供しておらず、他の言語(C++のSTLも含めて)と比較したときの最大の欠点になっています。

そこで柴田望洋氏はそんな状況を打破すべく、C Magazineの連載で「文字列S1の途中idxに文字列S2を挿入する」というソースを紹介していました。

こんなソースです。

char *strinsstr(char *s1, int idx, const char *s2)
{
  int len1 = strlen(s1);        /* s1の文字数 */
  int len2 = strlen(s2);        /* s2の文字数 */

  if (idx <= len1 && len2 > 0) {
    int     i;
    for (i = len1 - idx; i >= 0; i--)
      s1[idx + i + len2] = s1[idx + i];
    for (i = 0; i < len2; i++)
      s1[idx + i] = s2[i];
  }
  return (s1);
}

これは結構便利だと思い、自分なりに実装し直してみることにしました。

続きを読む "文字列操作関数を自分なりに改善してみる"

| | コメント (0) | トラックバック (0)

2007年8月20日 (月)

main()もスレッドの1つだった

久しぶりの技術ネタです。

マルチスレッドプログラミングの用語として「メインスレッド」というのがあります。このスレッドが終了すると、現在動作している他スレッドも含めて全て終了する、という属性を持つスレッドです。

Pthreadsにおいては、main()で始まる処理の流れがメインスレッドとして紹介されています。

・・・が、どうもmain()は必ずしもメインスレッドではないことに気づきました。
(以下、つまらない人には全く面白くない話が続きます)

続きを読む "main()もスレッドの1つだった"

| | コメント (0) | トラックバック (0)

2007年6月19日 (火)

バッファオーバーフローの傾向と達人の視点

Manju

写真は以前妻が買ってきたまんじゅうに書いてあった注意書きです。「絶対に」って・・・。何かクレームでもあったんでしょうか(確かに固くなりそうだったし)。

仕事で作っているプログラム(マルチスレッド)で2000回くらいの試行(ここで約20時間経過)に1回くらいのペースでSegmentation Faultが発生しています。困った。

もうハイゼンバグもいいとこで、再現させるのも一苦労なら、LibSegFaultを使ってバックトレースを取ってもレジスタの値すらメチャクチャになっている、という有様。はっきりいってあきらめてました。

ところが、それでもプロジェクトメンバーの一人がじっとエラーログを調べ続け、

 「このレジスタ、文字列になってません?」

と言い出しました。何を言っているんだ?と思って聞いてみると、レジスタに表示されている16進文字列がキャラクタコード上で意味のありそうな4文字になっている、とのこと。例えばこんな感じに。

レジスタA:「0x6E69614D」→「Main」(Little Endianに注意)

見てみると、本当だ!確かに!

いくつかのレジスタが文字列の一部を示しているような状態になっていました。そこでこの文字列をヒープ領域に書き込みそうな処理を探して、その開発担当者に情報出し。
調査が一気に進みました。

正直言って、この意見を聞いて寒気がしました。アドレス情報に見えて実は文字列になっているという考え方はこれまで全く持っていませんでしたので。
「どうしてこんなことに気がついたんですか?」と本人に聞くと、

  • バッファが上書きされるなら、文字列である可能性が高い
  • アドレス値がキャラクタコードでの[0-9a-zA-Z]に相当する値だった

なのだそうです。言われてみれば確かにそうで、バッファオーバーフローやアンダーフロー時の兆候を見るテクニックになりそうですが、普通レジスタはアドレスを示しますから、そんなのまず気づかないですよ。

自分にとって、考えが本当に広がる経験でした。ちなみにこの人は他にも色々と活躍していて、一緒にプロジェクトを進められて本当に幸せだな、と思います。

| | コメント (6) | トラックバック (0)

2007年6月 8日 (金)

メモリ破壊チェックツール

Struggling メモリ破壊や、メモリリークを調べる場合,Linux状では通常Electric Fenceやdmalloc、valgrindなどを使用します。メモリリーク、ダブルフリーはもちろん、valgrindは領域を超えたアクセスなどまで検出してくれる優れものです。

しかしこれらは、組み込み機器のようなメモリの少ない、遅い環境で使うことは難しいのが実情です。バイナリをROMに持ってくることすらできません。そのため、メモリエラーの類が発生した場合はソースレビューか、コンソールへのfprintf()(←printf()だとバッファリングでずれる)にどうしても頼ってしまいます。

(以下マニアックな話が続くので、残りは「続きを読む」に書きます)

続きを読む "メモリ破壊チェックツール"

| | コメント (0) | トラックバック (0)

より以前の記事一覧