[C] restrict修飾子

2016年6月20日月曜日

C

Cのstrict aliasingについてではポインタのエイリアシングを見たけど、C99では明示的に異なる領域を指していることを示すためにrestrict修飾子が導入されている。

# 残念ながらrestrictはC++では導入されていない。なので、これを使うとC++とは互換性のないコードになってしまう。。。

例えば、以下のようにchar配列の内容を反転することを考えよう。
void reverse(char * dest, const char * src, size_t n) {
    for (size_t i = 0; i < n; i++) {
        dest[i] = src[n - i - 1];
    }
}
reverse関数のsrcとdestが異なる領域を指していればこれはうまくいく。

でも、この関数を呼び出す側がsrcとdestに同じアドレスを渡して、"その場で"配列を反転したいとしたらどうだろうか?
この場合はreverse関数の中で反転用のバッファ領域が必要になってしまう。

ここで"その場"での反転もサポートするかどうかの問題が出てくるけど、サポートしないと決めたとしよう。この場合、srcとdestは必ず異なる領域を指しているという前提を置いていいということを示すためにsrcとdestにrestrictを付けることができる。
void reverse(char * restrict dest, const char * restrict src, size_t n) {
    for (size_t i = 0; i < n; i++) {
        dest[i] = src[n - i - 1];
    }
}
# ただし、本当に領域が重複していないかは呼び出し側の責任である!

restrictを付けることによって、コンパイラも領域が重複していないことを前提とした最適化が可能となる。
# というより、本来こっちが主な目的なんだけど。

実際、C99以降ではmemmoveとmemcpyの定義は以下となっている。
// コピー元とコピー先が重複していないこと!
void *memcpy(void *restrict dst, const void *restrict src, size_t n);

// コピー元とコピー先が重複していてもOK!
void *memmove(void *dst, const void *src, size_t len);