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

プロフィール 

Author:高木信尚

ホームページ
ブログ

最近の記事 

最近のコメント 

最近のトラックバック 

月別アーカイブ 

カテゴリー 

ブロとも申請フォーム 

この人とブロともになる

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

 

2008/10/07 11:44|

 

memmove関数は、memcpy関数とほとんど同じなのですが、コピー元とコピー先の領域が重なっていても構わない点が異なります。したがって、memcpyでは付けていたrestrict修飾子がmemmoveでは付きません。では、早速コードを見てみましょう。

#include <stddef.h>

void *memmove(void *s1, const void *s2, size_t n)
{
  register char *ss1 = s1;
  register const char *ss2 = s2;
  if (n != 0)
  {
    if (ss1 < ss2)
    {
      register const char *t = ss2 + n;
      do
        *ss1++ = *ss2++;
      while (ss2 != t);
    }
    else if (ss1 > ss2)
    {
      register const char *t = ss2;
      ss1 += n;
      ss2 += n;
      do
        *--ss1 = *--ss2;
      while (ss2 != t);
    }
  }
  return s1;
}

領域が重なっている場合、コピー先の終端部分に重なっているのか、先頭部分に重なっているのかによって、配列の最初からコピーするか最後からコピーするかを切り替えています。

配列の最後からコピーする場合(ss1 > ss2の条件)、ss1およびss2を配列の最後の要素を一つ越えたところを指すように初期化しています。そして、tはコピー元の配列の最初を指しています。これは、ポインタに整数値を加減算するとき、結果のポインタは、同じ配列内の要素を指すか、配列の最後の要素を一つ越えたところを指さなければならないからです。結果がそれらの範囲に収まらない場合、すなわちオーバーフローした場合の動作は未定義になります。

また、ss1とss2の比較ですが、ss1とss2は同じ集成体(配列または構造体)を指しているとは限らないため、厳密にいえば、この部分は未定義の動作になってしまいます。ただ、効率と可読性を考えると、他に妥当な手もないので、こうしています。コンパイル結果を調べて、期待通りに展開されていることは確認しています。
2006/03/23 12:41|文字列操作TB:0CM:4

コメント

特定の処理系のためのコードと考えていいですか。

処理系を意識しないのであれば、
memmove 関数の
異なるポインタの比較は
できないのではないでしょうか。

C99では比較してもよくなった?
#-|2006/04/20(木) 00:17 [ 編集 ]


コメントありがとうございます。

> 特定の処理系のためのコードと考えていいですか。
基本的にはそうです。処理系を特定しないと実装できない関数も少なくないので。memmoveも例外ではありません。

> 異なるポインタの比較は
> できないのではないでしょうか。
ss1とss2の比較の部分でしょうか?
まず、型の違いに関しては、修飾子の有無は問題ありません。
次に、同一の集成体に属さないポインタどうしの比較は未定義ですが、コンパイル結果を見て、期待通りに展開されていることを確認しています。

> C99では比較してもよくなった?
C99でも事情は同じです。
この部分に関して、効率と可読性を犠牲にして、規格厳密合致プログラムにする必要はないと考えています。
たかぎ #ftr86F3A|2006/04/20(木) 00:39 [ 編集 ]


先程は、Name も入れずにすいませんでした。
(送信できてしまって驚きました。)

ポインタの比較に関する私の考えは、
「型が同じでも異なるポインタは、比較できない処理系がある」 です。
また、「型が同じで同じポインタから連続することが保証されているポインタは比較できる」 です。

そのため、
どの処理系でも
memmove 関数の
s1 と ss1 は比較OK、
s2 と ss2 と t は比較OK、
ss1 と ss2 の比較は処理系に依存する
ということになると考えています。

特定の処理系以外でも
技術的にとても参考になるHPだと思います。
他の人にもそうだと思います。

ポインタについては
誤解する人も多いので
memmove 関数での実装については、
誤解しないように注意を促した方が良いと思います。
かわぎた #-|2006/04/20(木) 02:09 [ 編集 ]


> 「型が同じでも異なるポインタは、比較できない処理系がある」 です。
これは要するに、同じ集成体(配列や構造体)の中を指しているのではないポインタの比較はできないということですね。
確かにこれは、規格上未定義になります。

他の部分では、可能な限り未定義の動作を避けるようにしているのですが、ここだけはどうしようもありませんでした。
未定義にならないようにするには、要素を1つずつ等価演算子で比較(等価演算子であれば、どんなポインタを比較しても未定義にならない)しながら、重複領域があるかどうかをまず調べ、それからコピーすることになると思いますが、効率も可読性も非常に低下します。

> 誤解しないように注意を促した方が良いと思います。
そうですね。
念のため一言入れておきます。
たかぎ #ftr86F3A|2006/04/20(木) 09:35 [ 編集 ]

コメントの投稿

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


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

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

ブログ内検索 

お勧め書籍 

RSSフィード 

リンク 

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

Powered By FC2ブログ 

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

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