【C言語】
配列をずらす時memcpyは
未定義動作のバグ
memmoveを使おう

memcpy 配列をずらす時 memmove

warning: ‘builtin_memcpy’ accessing 9 bytes at offsets 1 and 0 overlaps 8 bytes at offset 1

警告:転送領域が重なったのでrestrict修飾違反で未定義動作
[-Wrestrict]

■memmoveとは

memmoveとは
名前にmoveとありますが
メモリを移動ではなくてコピーします。

似たような関数にmemcpyがあります。
memcpyとmemmoveの違いは
memcpy:
➡メモリコピー領域が重なっていては駄目
memmove:
➡メモリコピー領域が重なっていてもOK

ここではメモリコピー領域が重なった
配列をずらす事例を紹介します。

■配列をずらす時memcpyを使うと未定義動作

#include <stdio.h>
#include <string.h>

int  main(void){
    char    buf[]="0123456789";
    memcpy(&buf[1],&buf[0],9);
    printf("%s\n",buf);
}

memcpy( )の転送領域が重なっているので
restrict 修飾違反となり未定義動作となります。

注意:

・最適化(ーOオプション使用)しないとこの警告は出ません。
・実験ではgccとclangの結果は異なりました。
・最適化レベルを変えると結果も変わりました。
※未定義動作なので環境によって結果が変わるかも


■配列をずらす時はmemmoveを使おう

#include <stdio.h>
#include <string.h>

int  main(void){
    char    buf[]="0123456789";
    memmove(&buf[1],&buf[0],9);
    printf("%s\n",buf);
}

memcpy( )の代わりに
memmove( )を使用すると安全に動作します。


■sprintfの失敗例(入出力が同じ)

warning: ‘sprintf’ argument 4 overlaps destination object ‘output’

警告:sprintfの入力引数が出力引数と同じ
[-Wrestrict]

#include    <stdio.h>
int main(void)
{
    char    output[1024] = "filename";
    char    input[]      = "dirname" ;

    sprintf(output,"%s/%s",input,output);
    printf("%s\n",output);
}

出力バッファの第1引数outputと
入力バッファの第4引数outputが同じなので
未定義動作のバグとなります。

”dirname/filename”の出力を期待しても、
未定義動作なので何が表示されるかわかりません。
修正例を以下に示します。

■sprintfの修正例(入出力を分ける)

#include    <stdio.h>
int main(void) {
    char    output[1024];
    char    *fn = "filename";
    char    *dn = "dirname" ;

    sprintf(output,"%s/%s",dn,fn);
    printf("%s\n",output);
}

参考:

C-FAQ 11.25: memcpy()とmemmove()の違いは。

http://www.kouno.jp/home/c_faq/c11.html#25