C99に対応した標準Cライブラリの実装レポートを行っていきます。

プロフィール 

Author:高木信尚

ホームページ
ブログ

最近の記事 

最近のコメント 

最近のトラックバック 

月別アーカイブ 

カテゴリー 

ブロとも申請フォーム 

この人とブロともになる

ホーム 全記事一覧 << 前の記事 次の記事 >>

 

2008/10/07 11:45|

 

mem〜系の関数が続いていますが、今回はメモリブロックの比較を行うmemcmp関数です。strcmp関数とはループの終了条件が異なるだけですが、strcmp関数では、(必ず存在するはずの)ナル文字を比較対象とすることができたのに対して、memcmp関数では、最後の要素を1つ越えたところをアクセスすることができません。

例えば、strcmp関数と同じように書くなら、

int memcmp(const void *s1, const void *s2, size_t n)
{
  register const unsigned char *ss1, *ss2, *t;
  for (ss1 = s1, ss2 = s2, t = ss2 + n;
       *ss1 == *ss2 && ss2 != t;
       ss1++, ss2++)
    ;
  return *ss1 - *ss2;
}

となるわけですが、これだと必ずメモリブロックを外れたところをアクセスしてしまうため、動作が未定義になります。では、

  for (ss1 = s1, ss2 = s2, t = ss2 + n - 1;
       ss2 != t && *ss1 == *ss2;

とすればよい気もしますが、ポインタは配列の「最後の」要素を1つ越えたところを指すことはできても、「最初の」要素の1つ前を指すことはできません(未定義の動作になります)。

というわけで、今回は次のように記述しました。

#include <stddef.h>

int memcmp(const void *s1, const void *s2, size_t n)
{
  register const unsigned char *ss1, *ss2, *t;
  int result = 0;
  for (ss1 = s1, ss2 = s2, t = ss2 + n;
       ss2 != t && (result = *ss1 - *ss2) == 0;
       ss1++, ss2++)
    ;
  return result;
}

これで、とりあえず上で挙げた問題はクリアできたと思います。また、効率的にもさほど問題なさそうです。
実際にはここまでシビアに考えなくても動作する気もしますが、他の処理系に移植するときのことなどを考えると、未定義の動作は避けておいた方が無難でしょう。
2006/04/02 10:19|文字列操作TB:0CM:2

コメント

ついでにこちらも指摘。
最初の二つのコードは未定義動作が含まれているから避けた方がいいのではなく、(未定義動作に関係なく)間違った値を返すことがある。

#define FOO "_ABABC"
memcmp(FOO+1, FOO+3, 2);
memcmp(FOO+1, FOO+2, 0);

マイケル #-|2006/04/13(木) 23:20 [ 編集 ]


ご指摘ありがとうございます。

> memcmp(FOO+1, FOO+3, 2);

こちらは確かにダメですね。

> memcmp(FOO+1, FOO+2, 0);

こちらは、期待通りの値(0)だと思うのですが、何か勘違いしていますか?
たかぎ #ftr86F3A|2006/04/13(木) 23:40 [ 編集 ]

コメントの投稿

管理者にだけ表示を許可する


トラックバック
トラックバックURLはこちら
http://libc.blog47.fc2.com/tb.php/40-484b59e8

ホーム 全記事一覧 << 前の記事 次の記事 >>

ブログ内検索 

お勧め書籍 

RSSフィード 

リンク 

このブログをリンクに追加する

Powered By FC2ブログ 

Powered By FC2ブログ
ブログやるならFC2ブログ

Copyright(C) 2006 TAKAGI Nobuhisa All rights reserved.
Powered by FC2ブログ. 無料ホームページ アフィリエイト レンタルサーバー FC2ブログ 一戸建て template designed by 遥かなるわらしべ長者への挑戦.