string.c void* memmove(void *dst, const void *src, uint n)

トップページ
jupiteroak.hatenablog.com


string.c
https://github.com/mit-pdos/xv6-public/blob/master/string.c#L31

void* memmove(void *dst, const void *src, uint n)
{
  const char *s;
  char *d;

  s = src;
  d = dst;
  if(s < d && s + n > d){
    s += n;
    d += n;
    while(n-- > 0)
      *--d = *--s;
  } else
    while(n-- > 0)
      *d++ = *s++;

  return dst;
}

memmove関数は、先頭アドレスが引数src・サイズが引数nのメモリ領域(読み取り対象となるメモリ領域)から、先頭アドレスが引数dst・サイズが引数のメモリ領域(書き込み対象となるメモリ領域)へ、データをコピーします。

引数 dst
書き込み対象となるメモリ領域の先頭アドレスです。

引数 src
読み取り対象となるメモリ領域の先頭アドレスです。

引数 n
書き込み・読み取り対象となるメモリ領域のサイズ(バイト単位)です。

戻り値
引数 dstです。


処理の内容

アドレスsが下位側・アドレスdが上位側にあり かつ 2つのメモリ領域間で重なりがある場合

if(s < d && s + n > d){
    s += n;
    d += n;
    while(n-- > 0)
      *--d = *--s;
  } 

s < d は、読み取り対象となるメモリ領域の先頭アドレスsが下位側、書き込み対象となるメモリ領域の先頭アドレスdが上位側にある場合に、真となります。
s + n > dは、読み取り対象となるメモリ領域の終端アドレス+1の値(s+n)が、書き込み対象となるメモリ領域の先頭アドレスdより上位側にある場合に(読み取り対象となるメモリ領域と書き込み対象となるメモリ領域が重なっている場合に)、真となります。

終端アドレスから先頭アドレスへ向かう順番でデータをコピーする

s += n;
d += n;
while(n-- > 0)
   *--d = *--s;

s < d && s + n > d が真となる場合は(アドレスsが下位側・アドレスdが上位側にあり かつ 2つのメモリ領域間で重なりがある場合は)、終端アドレスから先頭アドレスへ向かう順番で、読み取り対象となるメモリ領域から書き込み対象となるメモリ領域へ、1バイトずつデータをコピーします。
読み取り対象となるメモリ領域と書き込み対象となるメモリ領域が重なっている状態のまま、先頭アドレスから終端アドレスへ向かう順番でデータをコピーすると、読み取り対象となるメモリ領域の前方部分の値を使って、書き込み対象となるメモリ領域の前方部分の値(読み取り対象となるメモリ領域の後方部分)を上書きしてしまい、正常なコピーができなくなってしまうからです。

s += n;
d += n;

終端アドレスから先頭アドレスへ向かう順番で1バイトずつデータをコピーするために、各メモリ領域の先頭アドレスを格納したポインタsとポインタdにnを加算します。

while(n-- > 0)
   *--d = *--s;

終端アドレスから先頭アドレスへ向かう順番で、読み取り対象となるメモリ領域から書き込み対象となるメモリ領域へ、1バイトずつデータをコピーします。1週目のループの時点では、sは読み取り対象となるメモリ領域の終端アドレス+1の値(s+n)、dは読み取り対象となるメモリ領域の終端アドレス+1の値(d+n)、となっているので、前置減分演算子を用いて式が評価される前にデクリメントします。

ループ1 ループ2 ... ループn-1 ループn ループ n+1
nの評価 n n-1 ... 2 1 0
n--の評価 n n-1 ... 2 1 0
--sの評価 s+n-1(終端アドレス) s+n-2 ... s+1 s(先頭アドレス) s-1
--dの評価 d+n-1(終端アドレス) d+n-2 ... d+1 d(先頭アドレス) d-1

上記以外の場合

} else
while(n-- > 0) *d++ = *s++;
}

読み取り対象となるメモリ領域と書き込み対象となるメモリ領域が重なっていない場合は、先頭から順に1バイトずつ読み取りと書き込みを行なっていきます。

先頭アドレスから終端アドレスへ向かう順番でデータをコピーする

while(n-- > 0) *d++ = *s++;
ループ1 ループ2 ... ループn-1 ループn ループn+1
nの評価 n n-1 ... 2 1 0
n--の評価 n n-1 ... 2 1 0
s++の評価 s(先頭アドレス) s+1 ... s+n-2 s+n-1(終端アドレス) s+n
d++の評価 d(先頭アドレス) d+1 ... d+n-2 d+n-1(終端アドレス) d+n

return dst;

引数dst(アドレス)を戻り値としてリターンします。